From f4be2026dfb507e5db919cc5df8fd934e05fa0b8 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 8 Sep 2015 15:53:46 -0700 Subject: std: Internalize almost all of `std::rt` This commit does some refactoring to make almost all of the `std::rt` private. Specifically, the following items are no longer part of its API: * DEFAULT_ERROR_CODE * backtrace * unwind * args * at_exit * cleanup * heap (this is just alloc::heap) * min_stack * util The module is now tagged as `#[doc(hidden)]` as the only purpose it's serve is an entry point for the `panic!` macro via the `begin_unwind` and `begin_unwind_fmt` reexports. --- src/libstd/collections/hash/table.rs | 13 +- src/libstd/io/lazy.rs | 4 +- src/libstd/panicking.rs | 3 +- src/libstd/process.rs | 2 +- src/libstd/rt.rs | 65 ++++++ src/libstd/rt/args.rs | 160 --------------- src/libstd/rt/at_exit_imp.rs | 82 -------- src/libstd/rt/backtrace.rs | 76 ------- src/libstd/rt/dwarf/eh.rs | 159 --------------- src/libstd/rt/dwarf/mod.rs | 107 ---------- src/libstd/rt/libunwind.rs | 145 ------------- src/libstd/rt/macros.rs | 51 ----- src/libstd/rt/mod.rs | 135 ------------- src/libstd/rt/unwind/gcc.rs | 235 --------------------- src/libstd/rt/unwind/mod.rs | 325 ------------------------------ src/libstd/rt/unwind/seh.rs | 145 ------------- src/libstd/rt/unwind/seh64_gnu.rs | 226 --------------------- src/libstd/rt/util.rs | 53 ----- src/libstd/sys/common/args.rs | 99 +++++++++ src/libstd/sys/common/at_exit_imp.rs | 82 ++++++++ src/libstd/sys/common/backtrace.rs | 65 +++++- src/libstd/sys/common/dwarf/eh.rs | 159 +++++++++++++++ src/libstd/sys/common/dwarf/mod.rs | 107 ++++++++++ src/libstd/sys/common/libunwind.rs | 145 +++++++++++++ src/libstd/sys/common/mod.rs | 48 ++++- src/libstd/sys/common/unwind/gcc.rs | 233 +++++++++++++++++++++ src/libstd/sys/common/unwind/mod.rs | 325 ++++++++++++++++++++++++++++++ src/libstd/sys/common/unwind/seh.rs | 146 ++++++++++++++ src/libstd/sys/common/unwind/seh64_gnu.rs | 218 ++++++++++++++++++++ src/libstd/sys/common/util.rs | 46 +++++ src/libstd/sys/unix/mod.rs | 14 ++ src/libstd/sys/unix/os.rs | 4 +- src/libstd/sys/unix/stack_overflow.rs | 2 +- src/libstd/sys/windows/mod.rs | 2 + src/libstd/sys/windows/net.rs | 5 +- src/libstd/sys/windows/stack_overflow.rs | 2 +- src/libstd/sys/windows/thread_local.rs | 4 +- src/libstd/thread/mod.rs | 7 +- 38 files changed, 1773 insertions(+), 1926 deletions(-) create mode 100644 src/libstd/rt.rs delete mode 100644 src/libstd/rt/args.rs delete mode 100644 src/libstd/rt/at_exit_imp.rs delete mode 100644 src/libstd/rt/backtrace.rs delete mode 100644 src/libstd/rt/dwarf/eh.rs delete mode 100644 src/libstd/rt/dwarf/mod.rs delete mode 100644 src/libstd/rt/libunwind.rs delete mode 100644 src/libstd/rt/macros.rs delete mode 100644 src/libstd/rt/mod.rs delete mode 100644 src/libstd/rt/unwind/gcc.rs delete mode 100644 src/libstd/rt/unwind/mod.rs delete mode 100644 src/libstd/rt/unwind/seh.rs delete mode 100644 src/libstd/rt/unwind/seh64_gnu.rs delete mode 100644 src/libstd/rt/util.rs create mode 100644 src/libstd/sys/common/args.rs create mode 100644 src/libstd/sys/common/at_exit_imp.rs create mode 100644 src/libstd/sys/common/dwarf/eh.rs create mode 100644 src/libstd/sys/common/dwarf/mod.rs create mode 100644 src/libstd/sys/common/libunwind.rs create mode 100644 src/libstd/sys/common/unwind/gcc.rs create mode 100644 src/libstd/sys/common/unwind/mod.rs create mode 100644 src/libstd/sys/common/unwind/seh.rs create mode 100644 src/libstd/sys/common/unwind/seh64_gnu.rs create mode 100644 src/libstd/sys/common/util.rs (limited to 'src/libstd') diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index e3601f1349c..f8bd791f681 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -8,23 +8,20 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use self::BucketState::*; +use alloc::heap::{allocate, deallocate, EMPTY}; -use clone::Clone; use cmp; use hash::{Hash, Hasher}; -use iter::{Iterator, ExactSizeIterator}; -use marker::{Copy, Send, Sync, Sized, self}; +use marker; use mem::{align_of, size_of}; use mem; use num::wrapping::OverflowingOps; -use ops::{Deref, DerefMut, Drop}; -use option::Option; -use option::Option::{Some, None}; +use ops::{Deref, DerefMut}; use ptr::{self, Unique}; -use rt::heap::{allocate, deallocate, EMPTY}; use collections::hash_state::HashState; +use self::BucketState::*; + const EMPTY_BUCKET: u64 = 0; /// The raw hashtable, providing safe-ish access to the unzipped and highly diff --git a/src/libstd/io/lazy.rs b/src/libstd/io/lazy.rs index 5424fec8110..65667f24dda 100644 --- a/src/libstd/io/lazy.rs +++ b/src/libstd/io/lazy.rs @@ -12,8 +12,8 @@ use prelude::v1::*; use cell::Cell; use ptr; -use rt; use sync::{StaticMutex, Arc}; +use sys_common; pub struct Lazy { lock: StaticMutex, @@ -51,7 +51,7 @@ impl Lazy { // `Arc` allocation in our own internal box (it will get deallocated by // the at exit handler). Otherwise we just return the freshly allocated // `Arc`. - let registered = rt::at_exit(move || { + let registered = sys_common::at_exit(move || { let g = self.lock.lock(); let ptr = self.ptr.get(); self.ptr.set(1 as *mut _); diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index bb4375c1b88..9715939d644 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -13,9 +13,10 @@ use io::prelude::*; use any::Any; use cell::RefCell; -use rt::{backtrace, unwind}; use sys::stdio::Stderr; +use sys_common::backtrace; use sys_common::thread_info; +use sys_common::unwind; thread_local! { pub static LOCAL_STDERR: RefCell>> = { diff --git a/src/libstd/process.rs b/src/libstd/process.rs index 79f2d16fef4..5f5d5a69003 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -582,7 +582,7 @@ impl Child { /// to run. #[stable(feature = "rust1", since = "1.0.0")] pub fn exit(code: i32) -> ! { - ::rt::cleanup(); + ::sys_common::cleanup(); ::sys::os::exit(code) } diff --git a/src/libstd/rt.rs b/src/libstd/rt.rs new file mode 100644 index 00000000000..8be0c6f3b3d --- /dev/null +++ b/src/libstd/rt.rs @@ -0,0 +1,65 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Runtime services +//! +//! The `rt` module provides a narrow set of runtime services, +//! including the global heap (exported in `heap`) and unwinding and +//! backtrace support. The APIs in this module are highly unstable, +//! and should be considered as private implementation details for the +//! time being. + +#![unstable(feature = "rt", + reason = "this public module should not exist and is highly likely \ + to disappear", + issue = "0")] +#![doc(hidden)] + +use borrow::ToOwned; +use mem; +use sys; +use sys_common::thread_info::{self, NewThread}; +use sys_common; +use thread::{self, Thread}; + +// Reexport some of our utilities which are expected by other crates. +pub use sys_common::unwind::{begin_unwind, begin_unwind_fmt}; + +#[cfg(not(test))] +#[lang = "start"] +fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize { + sys::init(); + + let failed = unsafe { + let main_guard = sys::thread::guard::init(); + sys::stack_overflow::init(); + + // Next, set up the current Thread with the guard information we just + // created. Note that this isn't necessary in general for new threads, + // but we just do this to name the main thread and to give it correct + // info about the stack bounds. + let thread: Thread = NewThread::new(Some("
".to_owned())); + thread_info::set(main_guard, thread); + + // Store our args if necessary in a squirreled away location + sys_common::args::init(argc, argv); + + // Let's run some code! + let res = thread::catch_panic(mem::transmute::<_, fn()>(main)); + sys_common::cleanup(); + res.is_err() + }; + + if failed { + 101 + } else { + 0 + } +} diff --git a/src/libstd/rt/args.rs b/src/libstd/rt/args.rs deleted file mode 100644 index 8de713aeac8..00000000000 --- a/src/libstd/rt/args.rs +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Global storage for command line arguments -//! -//! The current incarnation of the Rust runtime expects for -//! the processes `argc` and `argv` arguments to be stored -//! in a globally-accessible location for use by the `os` module. -//! -//! Only valid to call on Linux. Mac and Windows use syscalls to -//! discover the command line arguments. -//! -//! FIXME #7756: Would be nice for this to not exist. - -use vec::Vec; - -/// One-time global initialization. -pub unsafe fn init(argc: isize, argv: *const *const u8) { imp::init(argc, argv) } - -/// One-time global cleanup. -pub unsafe fn cleanup() { imp::cleanup() } - -/// Take the global arguments from global storage. -pub fn take() -> Option>> { imp::take() } - -/// Give the global arguments to global storage. -/// -/// It is an error if the arguments already exist. -pub fn put(args: Vec>) { imp::put(args) } - -/// Make a clone of the global arguments. -pub fn clone() -> Option>> { imp::clone() } - -#[cfg(any(target_os = "linux", - target_os = "android", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "bitrig", - target_os = "netbsd", - target_os = "openbsd"))] -mod imp { - use prelude::v1::*; - - use libc; - use mem; - use ffi::CStr; - - use sync::StaticMutex; - - static mut GLOBAL_ARGS_PTR: usize = 0; - static LOCK: StaticMutex = StaticMutex::new(); - - pub unsafe fn init(argc: isize, argv: *const *const u8) { - let args = load_argc_and_argv(argc, argv); - put(args); - } - - pub unsafe fn cleanup() { - take(); - } - - pub fn take() -> Option>> { - let _guard = LOCK.lock(); - unsafe { - let ptr = get_global_ptr(); - let val = mem::replace(&mut *ptr, None); - val.as_ref().map(|s: &Box>>| (**s).clone()) - } - } - - pub fn put(args: Vec>) { - let _guard = LOCK.lock(); - unsafe { - let ptr = get_global_ptr(); - rtassert!((*ptr).is_none()); - (*ptr) = Some(box args.clone()); - } - } - - pub fn clone() -> Option>> { - let _guard = LOCK.lock(); - unsafe { - let ptr = get_global_ptr(); - (*ptr).as_ref().map(|s: &Box>>| (**s).clone()) - } - } - - fn get_global_ptr() -> *mut Option>>> { - unsafe { mem::transmute(&GLOBAL_ARGS_PTR) } - } - - unsafe fn load_argc_and_argv(argc: isize, - argv: *const *const u8) -> Vec> { - let argv = argv as *const *const libc::c_char; - (0..argc).map(|i| { - CStr::from_ptr(*argv.offset(i)).to_bytes().to_vec() - }).collect() - } - - #[cfg(test)] - mod tests { - use prelude::v1::*; - - use super::*; - - #[test] - fn smoke_test() { - // Preserve the actual global state. - let saved_value = take(); - - let expected = vec![ - b"happy".to_vec(), - b"today?".to_vec(), - ]; - - put(expected.clone()); - assert!(clone() == Some(expected.clone())); - assert!(take() == Some(expected.clone())); - assert!(take() == None); - - // Restore the actual global state. - match saved_value { - Some(ref args) => put(args.clone()), - None => () - } - } - } -} - -#[cfg(any(target_os = "macos", - target_os = "ios", - target_os = "windows"))] -mod imp { - use vec::Vec; - - pub unsafe fn init(_argc: isize, _argv: *const *const u8) { - } - - pub fn cleanup() { - } - - pub fn take() -> Option>> { - panic!() - } - - pub fn put(_args: Vec>) { - panic!() - } - - pub fn clone() -> Option>> { - panic!() - } -} diff --git a/src/libstd/rt/at_exit_imp.rs b/src/libstd/rt/at_exit_imp.rs deleted file mode 100644 index 7a1215bf382..00000000000 --- a/src/libstd/rt/at_exit_imp.rs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation of running at_exit routines -//! -//! Documentation can be found on the `rt::at_exit` function. - -use alloc::boxed::FnBox; -use boxed::Box; -use ptr; -use sys_common::mutex::Mutex; -use vec::Vec; - -type Queue = Vec>; - -// NB these are specifically not types from `std::sync` as they currently rely -// on poisoning and this module needs to operate at a lower level than requiring -// the thread infrastructure to be in place (useful on the borders of -// initialization/destruction). -static LOCK: Mutex = Mutex::new(); -static mut QUEUE: *mut Queue = ptr::null_mut(); - -// The maximum number of times the cleanup routines will be run. While running -// the at_exit closures new ones may be registered, and this count is the number -// of times the new closures will be allowed to register successfully. After -// this number of iterations all new registrations will return `false`. -const ITERS: usize = 10; - -unsafe fn init() -> bool { - if QUEUE.is_null() { - let state: Box = box Vec::new(); - QUEUE = Box::into_raw(state); - } else if QUEUE as usize == 1 { - // can't re-init after a cleanup - return false - } - - true -} - -pub fn cleanup() { - for i in 0..ITERS { - unsafe { - LOCK.lock(); - let queue = QUEUE; - QUEUE = if i == ITERS - 1 {1} else {0} as *mut _; - LOCK.unlock(); - - // make sure we're not recursively cleaning up - rtassert!(queue as usize != 1); - - // If we never called init, not need to cleanup! - if queue as usize != 0 { - let queue: Box = Box::from_raw(queue); - for to_run in *queue { - to_run(); - } - } - } - } -} - -pub fn push(f: Box) -> bool { - let mut ret = true; - unsafe { - LOCK.lock(); - if init() { - (*QUEUE).push(f); - } else { - ret = false; - } - LOCK.unlock(); - } - ret -} diff --git a/src/libstd/rt/backtrace.rs b/src/libstd/rt/backtrace.rs deleted file mode 100644 index 9e7ed89bae1..00000000000 --- a/src/libstd/rt/backtrace.rs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Simple backtrace functionality (to print on panic) - -#![allow(non_camel_case_types)] - -use env; -use sync::atomic::{self, Ordering}; - -pub use sys::backtrace::write; - -// For now logging is turned off by default, and this function checks to see -// whether the magical environment variable is present to see if it's turned on. -pub fn log_enabled() -> bool { - static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0); - match ENABLED.load(Ordering::SeqCst) { - 1 => return false, - 2 => return true, - _ => {} - } - - let val = match env::var_os("RUST_BACKTRACE") { - Some(..) => 2, - None => 1, - }; - ENABLED.store(val, Ordering::SeqCst); - val == 2 -} - -#[cfg(test)] -mod tests { - use prelude::v1::*; - use sys_common; - macro_rules! t { ($a:expr, $b:expr) => ({ - let mut m = Vec::new(); - sys_common::backtrace::demangle(&mut m, $a).unwrap(); - assert_eq!(String::from_utf8(m).unwrap(), $b); - }) } - - #[test] - fn demangle() { - t!("test", "test"); - t!("_ZN4testE", "test"); - t!("_ZN4test", "_ZN4test"); - t!("_ZN4test1a2bcE", "test::a::bc"); - } - - #[test] - fn demangle_dollars() { - t!("_ZN4$RP$E", ")"); - t!("_ZN8$RF$testE", "&test"); - t!("_ZN8$BP$test4foobE", "*test::foob"); - t!("_ZN9$u20$test4foobE", " test::foob"); - } - - #[test] - fn demangle_many_dollars() { - t!("_ZN13test$u20$test4foobE", "test test::foob"); - t!("_ZN12test$BP$test4foobE", "test*test::foob"); - } - - #[test] - fn demangle_windows() { - t!("ZN4testE", "test"); - t!("ZN13test$u20$test4foobE", "test test::foob"); - t!("ZN12test$RF$test4foobE", "test&test::foob"); - } -} diff --git a/src/libstd/rt/dwarf/eh.rs b/src/libstd/rt/dwarf/eh.rs deleted file mode 100644 index f4799703d99..00000000000 --- a/src/libstd/rt/dwarf/eh.rs +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Parsing of GCC-style Language-Specific Data Area (LSDA) -//! For details see: -//! http://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html -//! http://mentorembedded.github.io/cxx-abi/exceptions.pdf -//! http://www.airs.com/blog/archives/460 -//! http://www.airs.com/blog/archives/464 -//! -//! A reference implementation may be found in the GCC source tree -//! (/libgcc/unwind-c.c as of this writing) - -#![allow(non_upper_case_globals)] -#![allow(unused)] - -use prelude::v1::*; -use rt::dwarf::DwarfReader; -use core::mem; - -pub const DW_EH_PE_omit : u8 = 0xFF; -pub const DW_EH_PE_absptr : u8 = 0x00; - -pub const DW_EH_PE_uleb128 : u8 = 0x01; -pub const DW_EH_PE_udata2 : u8 = 0x02; -pub const DW_EH_PE_udata4 : u8 = 0x03; -pub const DW_EH_PE_udata8 : u8 = 0x04; -pub const DW_EH_PE_sleb128 : u8 = 0x09; -pub const DW_EH_PE_sdata2 : u8 = 0x0A; -pub const DW_EH_PE_sdata4 : u8 = 0x0B; -pub const DW_EH_PE_sdata8 : u8 = 0x0C; - -pub const DW_EH_PE_pcrel : u8 = 0x10; -pub const DW_EH_PE_textrel : u8 = 0x20; -pub const DW_EH_PE_datarel : u8 = 0x30; -pub const DW_EH_PE_funcrel : u8 = 0x40; -pub const DW_EH_PE_aligned : u8 = 0x50; - -pub const DW_EH_PE_indirect : u8 = 0x80; - -#[derive(Copy, Clone)] -pub struct EHContext { - pub ip: usize, // Current instruction pointer - pub func_start: usize, // Address of the current function - pub text_start: usize, // Address of the code section - pub data_start: usize, // Address of the data section -} - -pub unsafe fn find_landing_pad(lsda: *const u8, context: &EHContext) - -> Option { - if lsda.is_null() { - return None; - } - - let func_start = context.func_start; - let mut reader = DwarfReader::new(lsda); - - let start_encoding = reader.read::(); - // base address for landing pad offsets - let lpad_base = if start_encoding != DW_EH_PE_omit { - read_encoded_pointer(&mut reader, context, start_encoding) - } else { - func_start - }; - - let ttype_encoding = reader.read::(); - if ttype_encoding != DW_EH_PE_omit { - // Rust doesn't analyze exception types, so we don't care about the type table - reader.read_uleb128(); - } - - let call_site_encoding = reader.read::(); - let call_site_table_length = reader.read_uleb128(); - let action_table = reader.ptr.offset(call_site_table_length as isize); - // Return addresses point 1 byte past the call instruction, which could - // be in the next IP range. - let ip = context.ip-1; - - while reader.ptr < action_table { - let cs_start = read_encoded_pointer(&mut reader, context, call_site_encoding); - let cs_len = read_encoded_pointer(&mut reader, context, call_site_encoding); - let cs_lpad = read_encoded_pointer(&mut reader, context, call_site_encoding); - let cs_action = reader.read_uleb128(); - // Callsite table is sorted by cs_start, so if we've passed the ip, we - // may stop searching. - if ip < func_start + cs_start { - break - } - if ip < func_start + cs_start + cs_len { - if cs_lpad != 0 { - return Some(lpad_base + cs_lpad); - } else { - return None; - } - } - } - // IP range not found: gcc's C++ personality calls terminate() here, - // however the rest of the languages treat this the same as cs_lpad == 0. - // We follow this suit. - None -} - -#[inline] -fn round_up(unrounded: usize, align: usize) -> usize { - assert!(align.is_power_of_two()); - (unrounded + align - 1) & !(align - 1) -} - -unsafe fn read_encoded_pointer(reader: &mut DwarfReader, - context: &EHContext, - encoding: u8) -> usize { - assert!(encoding != DW_EH_PE_omit); - - // DW_EH_PE_aligned implies it's an absolute pointer value - if encoding == DW_EH_PE_aligned { - reader.ptr = round_up(reader.ptr as usize, - mem::size_of::()) as *const u8; - return reader.read::(); - } - - let mut result = match encoding & 0x0F { - DW_EH_PE_absptr => reader.read::(), - DW_EH_PE_uleb128 => reader.read_uleb128() as usize, - DW_EH_PE_udata2 => reader.read::() as usize, - DW_EH_PE_udata4 => reader.read::() as usize, - DW_EH_PE_udata8 => reader.read::() as usize, - DW_EH_PE_sleb128 => reader.read_sleb128() as usize, - DW_EH_PE_sdata2 => reader.read::() as usize, - DW_EH_PE_sdata4 => reader.read::() as usize, - DW_EH_PE_sdata8 => reader.read::() as usize, - _ => panic!() - }; - - result += match encoding & 0x70 { - DW_EH_PE_absptr => 0, - // relative to address of the encoded value, despite the name - DW_EH_PE_pcrel => reader.ptr as usize, - DW_EH_PE_textrel => { assert!(context.text_start != 0); - context.text_start }, - DW_EH_PE_datarel => { assert!(context.data_start != 0); - context.data_start }, - DW_EH_PE_funcrel => { assert!(context.func_start != 0); - context.func_start }, - _ => panic!() - }; - - if encoding & DW_EH_PE_indirect != 0 { - result = *(result as *const usize); - } - - result -} diff --git a/src/libstd/rt/dwarf/mod.rs b/src/libstd/rt/dwarf/mod.rs deleted file mode 100644 index 822826bcc83..00000000000 --- a/src/libstd/rt/dwarf/mod.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Utilities for parsing DWARF-encoded data streams. -//! See http://www.dwarfstd.org, -//! DWARF-4 standard, Section 7 - "Data Representation" - -// This module is used only by x86_64-pc-windows-gnu for now, but we -// are compiling it everywhere to avoid regressions. -#![allow(unused)] - -pub mod eh; - -use prelude::v1::*; -use core::mem; - -pub struct DwarfReader { - pub ptr : *const u8 -} - -#[repr(C,packed)] -struct Unaligned(T); - -impl DwarfReader { - - pub fn new(ptr : *const u8) -> DwarfReader { - DwarfReader { - ptr : ptr - } - } - - // DWARF streams are packed, so e.g. a u32 would not necessarily be aligned - // on a 4-byte boundary. This may cause problems on platforms with strict - // alignment requirements. By wrapping data in a "packed" struct, we are - // telling the backend to generate "misalignment-safe" code. - pub unsafe fn read(&mut self) -> T { - let Unaligned(result) = *(self.ptr as *const Unaligned); - self.ptr = self.ptr.offset(mem::size_of::() as isize); - result - } - - // ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable - // Length Data". - pub unsafe fn read_uleb128(&mut self) -> u64 { - let mut shift : usize = 0; - let mut result : u64 = 0; - let mut byte : u8; - loop { - byte = self.read::(); - result |= ((byte & 0x7F) as u64) << shift; - shift += 7; - if byte & 0x80 == 0 { - break; - } - } - result - } - - pub unsafe fn read_sleb128(&mut self) -> i64 { - let mut shift : usize = 0; - let mut result : u64 = 0; - let mut byte : u8; - loop { - byte = self.read::(); - result |= ((byte & 0x7F) as u64) << shift; - shift += 7; - if byte & 0x80 == 0 { - break; - } - } - // sign-extend - if shift < 8 * mem::size_of::() && (byte & 0x40) != 0 { - result |= (!0 as u64) << shift; - } - result as i64 - } -} - -#[test] -fn dwarf_reader() { - let encoded: &[u8] = &[1, - 2, 3, - 4, 5, 6, 7, - 0xE5, 0x8E, 0x26, - 0x9B, 0xF1, 0x59, - 0xFF, 0xFF]; - - let mut reader = DwarfReader::new(encoded.as_ptr()); - - unsafe { - assert!(reader.read::() == u8::to_be(1u8)); - assert!(reader.read::() == u16::to_be(0x0203)); - assert!(reader.read::() == u32::to_be(0x04050607)); - - assert!(reader.read_uleb128() == 624485); - assert!(reader.read_sleb128() == -624485); - - assert!(reader.read::() == i8::to_be(-1)); - } -} diff --git a/src/libstd/rt/libunwind.rs b/src/libstd/rt/libunwind.rs deleted file mode 100644 index fde612014e9..00000000000 --- a/src/libstd/rt/libunwind.rs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Unwind library interface - -#![allow(non_upper_case_globals)] -#![allow(non_camel_case_types)] -#![allow(non_snake_case)] -#![allow(dead_code)] // these are just bindings - -#[cfg(any(not(target_arch = "arm"), target_os = "ios"))] -pub use self::_Unwind_Action::*; -#[cfg(target_arch = "arm")] -pub use self::_Unwind_State::*; -pub use self::_Unwind_Reason_Code::*; - -use libc; - -#[cfg(any(not(target_arch = "arm"), target_os = "ios"))] -#[repr(C)] -#[derive(Copy, Clone)] -pub enum _Unwind_Action { - _UA_SEARCH_PHASE = 1, - _UA_CLEANUP_PHASE = 2, - _UA_HANDLER_FRAME = 4, - _UA_FORCE_UNWIND = 8, - _UA_END_OF_STACK = 16, -} - -#[cfg(target_arch = "arm")] -#[repr(C)] -#[derive(Copy, Clone)] -pub enum _Unwind_State { - _US_VIRTUAL_UNWIND_FRAME = 0, - _US_UNWIND_FRAME_STARTING = 1, - _US_UNWIND_FRAME_RESUME = 2, - _US_ACTION_MASK = 3, - _US_FORCE_UNWIND = 8, - _US_END_OF_STACK = 16 -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub enum _Unwind_Reason_Code { - _URC_NO_REASON = 0, - _URC_FOREIGN_EXCEPTION_CAUGHT = 1, - _URC_FATAL_PHASE2_ERROR = 2, - _URC_FATAL_PHASE1_ERROR = 3, - _URC_NORMAL_STOP = 4, - _URC_END_OF_STACK = 5, - _URC_HANDLER_FOUND = 6, - _URC_INSTALL_CONTEXT = 7, - _URC_CONTINUE_UNWIND = 8, - _URC_FAILURE = 9, // used only by ARM EABI -} - -pub type _Unwind_Exception_Class = u64; - -pub type _Unwind_Word = libc::uintptr_t; - -#[cfg(target_arch = "x86")] -pub const unwinder_private_data_size: usize = 5; - -#[cfg(target_arch = "x86_64")] -pub const unwinder_private_data_size: usize = 6; - -#[cfg(all(target_arch = "arm", not(target_os = "ios")))] -pub const unwinder_private_data_size: usize = 20; - -#[cfg(all(target_arch = "arm", target_os = "ios"))] -pub const unwinder_private_data_size: usize = 5; - -#[cfg(target_arch = "aarch64")] -pub const unwinder_private_data_size: usize = 2; - -#[cfg(any(target_arch = "mips", target_arch = "mipsel"))] -pub const unwinder_private_data_size: usize = 2; - -#[cfg(target_arch = "powerpc")] -pub const unwinder_private_data_size: usize = 2; - -#[repr(C)] -pub struct _Unwind_Exception { - pub exception_class: _Unwind_Exception_Class, - pub exception_cleanup: _Unwind_Exception_Cleanup_Fn, - pub private: [_Unwind_Word; unwinder_private_data_size], -} - -pub enum _Unwind_Context {} - -pub type _Unwind_Exception_Cleanup_Fn = - extern "C" fn(unwind_code: _Unwind_Reason_Code, - exception: *mut _Unwind_Exception); - -#[cfg(any(all(target_os = "linux", not(target_env = "musl")), - target_os = "freebsd"))] -#[link(name = "gcc_s")] -extern {} - -#[cfg(all(target_os = "linux", target_env = "musl", not(test)))] -#[link(name = "unwind", kind = "static")] -extern {} - -#[cfg(any(target_os = "android", target_os = "netbsd", target_os = "openbsd"))] -#[link(name = "gcc")] -extern {} - -#[cfg(target_os = "dragonfly")] -#[link(name = "gcc_pic")] -extern {} - -#[cfg(target_os = "bitrig")] -#[link(name = "c++abi")] -extern {} - -extern "C" { - // iOS on armv7 uses SjLj exceptions and requires to link - // against corresponding routine (..._SjLj_...) - #[cfg(not(all(target_os = "ios", target_arch = "arm")))] - pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) - -> _Unwind_Reason_Code; - - #[cfg(all(target_os = "ios", target_arch = "arm"))] - fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) - -> _Unwind_Reason_Code; - - pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception); -} - -// ... and now we just providing access to SjLj counterspart -// through a standard name to hide those details from others -// (see also comment above regarding _Unwind_RaiseException) -#[cfg(all(target_os = "ios", target_arch = "arm"))] -#[inline(always)] -pub unsafe fn _Unwind_RaiseException(exc: *mut _Unwind_Exception) - -> _Unwind_Reason_Code { - _Unwind_SjLj_RaiseException(exc) -} diff --git a/src/libstd/rt/macros.rs b/src/libstd/rt/macros.rs deleted file mode 100644 index 414ccc911af..00000000000 --- a/src/libstd/rt/macros.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Macros used by the runtime. -//! -//! These macros call functions which are only accessible in the `rt` module, so -//! they aren't defined anywhere outside of the `rt` module. - -macro_rules! rterrln { - ($fmt:expr) => ( { - ::rt::util::dumb_print(format_args!(concat!($fmt, "\n"))) - } ); - ($fmt:expr, $($arg:expr),*) => ( { - ::rt::util::dumb_print(format_args!(concat!($fmt, "\n"), $($arg),*)) - } ) -} - -// Some basic logging. Enabled by passing `--cfg rtdebug` to the libstd build. -macro_rules! rtdebug { - ($arg:expr) => ( { - if cfg!(rtdebug) { - rterrln!($arg) - } - } ); - ($str:expr, $($arg:expr),*) => ( { - if cfg!(rtdebug) { - rterrln!($str, $($arg),*) - } - }) -} - -macro_rules! rtassert { - ( $arg:expr ) => ( { - if ::rt::util::ENFORCE_SANITY { - if !$arg { - rtabort!(" assertion failed: {}", stringify!($arg)); - } - } - } ) -} - -macro_rules! rtabort { - ($($arg:tt)*) => (::rt::util::abort(format_args!($($arg)*))) -} diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs deleted file mode 100644 index 95cba132201..00000000000 --- a/src/libstd/rt/mod.rs +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Runtime services -//! -//! The `rt` module provides a narrow set of runtime services, -//! including the global heap (exported in `heap`) and unwinding and -//! backtrace support. The APIs in this module are highly unstable, -//! and should be considered as private implementation details for the -//! time being. - -#![unstable(feature = "rt", - reason = "this public module should not exist and is highly likely \ - to disappear", - issue = "0")] -#![allow(missing_docs)] - -use prelude::v1::*; -use sync::Once; -use sys; -use thread; - -// Reexport some of our utilities which are expected by other crates. -pub use self::util::min_stack; -pub use self::unwind::{begin_unwind, begin_unwind_fmt}; - -// Reexport some functionality from liballoc. -pub use alloc::heap; - -// Simple backtrace functionality (to print on panic) -pub mod backtrace; - -// Internals -#[macro_use] -mod macros; - -// These should be refactored/moved/made private over time -pub mod util; -pub mod unwind; -pub mod args; - -mod at_exit_imp; -mod libunwind; - -mod dwarf; - -/// The default error code of the rust runtime if the main thread panics instead -/// of exiting cleanly. -pub const DEFAULT_ERROR_CODE: isize = 101; - -#[cfg(not(test))] -#[lang = "start"] -fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize { - use prelude::v1::*; - - use mem; - use rt; - use sys_common::thread_info::{self, NewThread}; - use thread::Thread; - - let failed = unsafe { - let main_guard = sys::thread::guard::init(); - sys::stack_overflow::init(); - - // Next, set up the current Thread with the guard information we just - // created. Note that this isn't necessary in general for new threads, - // but we just do this to name the main thread and to give it correct - // info about the stack bounds. - let thread: Thread = NewThread::new(Some("
".to_owned())); - thread_info::set(main_guard, thread); - - // By default, some platforms will send a *signal* when a EPIPE error - // would otherwise be delivered. This runtime doesn't install a SIGPIPE - // handler, causing it to kill the program, which isn't exactly what we - // want! - // - // Hence, we set SIGPIPE to ignore when the program starts up in order - // to prevent this problem. - #[cfg(windows)] fn ignore_sigpipe() {} - #[cfg(unix)] fn ignore_sigpipe() { - use libc; - use libc::funcs::posix01::signal::signal; - unsafe { - assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != !0); - } - } - ignore_sigpipe(); - - // Store our args if necessary in a squirreled away location - args::init(argc, argv); - - // And finally, let's run some code! - let res = thread::catch_panic(mem::transmute::<_, fn()>(main)); - cleanup(); - res.is_err() - }; - - // If the exit code wasn't set, then the try block must have panicked. - if failed { - rt::DEFAULT_ERROR_CODE - } else { - 0 - } -} - -/// Enqueues a procedure to run when the main thread exits. -/// -/// Currently these closures are only run once the main *Rust* thread exits. -/// Once the `at_exit` handlers begin running, more may be enqueued, but not -/// infinitely so. Eventually a handler registration will be forced to fail. -/// -/// Returns `Ok` if the handler was successfully registered, meaning that the -/// closure will be run once the main thread exits. Returns `Err` to indicate -/// that the closure could not be registered, meaning that it is not scheduled -/// to be run. -pub fn at_exit(f: F) -> Result<(), ()> { - if at_exit_imp::push(Box::new(f)) {Ok(())} else {Err(())} -} - -/// One-time runtime cleanup. -pub fn cleanup() { - static CLEANUP: Once = Once::new(); - CLEANUP.call_once(|| unsafe { - args::cleanup(); - sys::stack_overflow::cleanup(); - at_exit_imp::cleanup(); - }); -} diff --git a/src/libstd/rt/unwind/gcc.rs b/src/libstd/rt/unwind/gcc.rs deleted file mode 100644 index 55deb048b7e..00000000000 --- a/src/libstd/rt/unwind/gcc.rs +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(private_no_mangle_fns)] - -use prelude::v1::*; - -use any::Any; -use rt::libunwind as uw; - -struct Exception { - uwe: uw::_Unwind_Exception, - cause: Option>, -} - -pub unsafe fn panic(data: Box) -> ! { - let exception: Box<_> = box Exception { - uwe: uw::_Unwind_Exception { - exception_class: rust_exception_class(), - exception_cleanup: exception_cleanup, - private: [0; uw::unwinder_private_data_size], - }, - cause: Some(data), - }; - let exception_param = Box::into_raw(exception) as *mut uw::_Unwind_Exception; - let error = uw::_Unwind_RaiseException(exception_param); - rtabort!("Could not unwind stack, error = {}", error as isize); - - extern fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code, - exception: *mut uw::_Unwind_Exception) { - rtdebug!("exception_cleanup()"); - unsafe { - let _: Box = Box::from_raw(exception as *mut Exception); - } - } -} - -pub unsafe fn cleanup(ptr: *mut u8) -> Box { - let my_ep = ptr as *mut Exception; - rtdebug!("caught {}", (*my_ep).uwe.exception_class); - let cause = (*my_ep).cause.take(); - uw::_Unwind_DeleteException(ptr as *mut _); - cause.unwrap() -} - -// Rust's exception class identifier. This is used by personality routines to -// determine whether the exception was thrown by their own runtime. -fn rust_exception_class() -> uw::_Unwind_Exception_Class { - // M O Z \0 R U S T -- vendor, language - 0x4d4f5a_00_52555354 -} - -// We could implement our personality routine in pure Rust, however exception -// info decoding is tedious. More importantly, personality routines have to -// handle various platform quirks, which are not fun to maintain. For this -// reason, we attempt to reuse personality routine of the C language: -// __gcc_personality_v0. -// -// Since C does not support exception catching, __gcc_personality_v0 simply -// always returns _URC_CONTINUE_UNWIND in search phase, and always returns -// _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase. -// -// This is pretty close to Rust's exception handling approach, except that Rust -// does have a single "catch-all" handler at the bottom of each thread's stack. -// So we have two versions of the personality routine: -// - rust_eh_personality, used by all cleanup landing pads, which never catches, -// so the behavior of __gcc_personality_v0 is perfectly adequate there, and -// - rust_eh_personality_catch, used only by rust_try(), which always catches. -// -// See also: rustc_trans::trans::intrinsic::trans_gnu_try - -#[cfg(all(not(target_arch = "arm"), - not(all(windows, target_arch = "x86_64")), - not(test)))] -pub mod eabi { - use rt::libunwind as uw; - use libc::c_int; - - extern { - fn __gcc_personality_v0(version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context) - -> uw::_Unwind_Reason_Code; - } - - #[lang = "eh_personality"] - #[no_mangle] - extern fn rust_eh_personality( - version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - unsafe { - __gcc_personality_v0(version, actions, exception_class, ue_header, - context) - } - } - - #[lang = "eh_personality_catch"] - #[no_mangle] - pub extern fn rust_eh_personality_catch( - version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - - if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase - uw::_URC_HANDLER_FOUND // catch! - } - else { // cleanup phase - unsafe { - __gcc_personality_v0(version, actions, exception_class, ue_header, - context) - } - } - } -} - -// iOS on armv7 is using SjLj exceptions and therefore requires to use -// a specialized personality routine: __gcc_personality_sj0 - -#[cfg(all(target_os = "ios", target_arch = "arm", not(test)))] -pub mod eabi { - use rt::libunwind as uw; - use libc::c_int; - - extern { - fn __gcc_personality_sj0(version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context) - -> uw::_Unwind_Reason_Code; - } - - #[lang = "eh_personality"] - #[no_mangle] - pub extern fn rust_eh_personality( - version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - unsafe { - __gcc_personality_sj0(version, actions, exception_class, ue_header, - context) - } - } - - #[lang = "eh_personality_catch"] - #[no_mangle] - pub extern fn rust_eh_personality_catch( - version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase - uw::_URC_HANDLER_FOUND // catch! - } - else { // cleanup phase - unsafe { - __gcc_personality_sj0(version, actions, exception_class, ue_header, - context) - } - } - } -} - - -// ARM EHABI uses a slightly different personality routine signature, -// but otherwise works the same. -#[cfg(all(target_arch = "arm", not(target_os = "ios"), not(test)))] -pub mod eabi { - use rt::libunwind as uw; - use libc::c_int; - - extern { - fn __gcc_personality_v0(state: uw::_Unwind_State, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context) - -> uw::_Unwind_Reason_Code; - } - - #[lang = "eh_personality"] - #[no_mangle] - extern fn rust_eh_personality( - state: uw::_Unwind_State, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - unsafe { - __gcc_personality_v0(state, ue_header, context) - } - } - - #[lang = "eh_personality_catch"] - #[no_mangle] - pub extern fn rust_eh_personality_catch( - state: uw::_Unwind_State, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - if (state as c_int & uw::_US_ACTION_MASK as c_int) - == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { // search phase - uw::_URC_HANDLER_FOUND // catch! - } - else { // cleanup phase - unsafe { - __gcc_personality_v0(state, ue_header, context) - } - } - } -} diff --git a/src/libstd/rt/unwind/mod.rs b/src/libstd/rt/unwind/mod.rs deleted file mode 100644 index 4feb2d49a98..00000000000 --- a/src/libstd/rt/unwind/mod.rs +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation of Rust stack unwinding -//! -//! For background on exception handling and stack unwinding please see -//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and -//! documents linked from it. -//! These are also good reads: -//! http://mentorembedded.github.io/cxx-abi/abi-eh.html -//! http://monoinfinito.wordpress.com/series/exception-handling-in-c/ -//! http://www.airs.com/blog/index.php?s=exception+frames -//! -//! ## A brief summary -//! -//! Exception handling happens in two phases: a search phase and a cleanup phase. -//! -//! In both phases the unwinder walks stack frames from top to bottom using -//! information from the stack frame unwind sections of the current process's -//! modules ("module" here refers to an OS module, i.e. an executable or a -//! dynamic library). -//! -//! For each stack frame, it invokes the associated "personality routine", whose -//! address is also stored in the unwind info section. -//! -//! In the search phase, the job of a personality routine is to examine exception -//! object being thrown, and to decide whether it should be caught at that stack -//! frame. Once the handler frame has been identified, cleanup phase begins. -//! -//! In the cleanup phase, personality routines invoke cleanup code associated -//! with their stack frames (i.e. destructors). Once stack has been unwound down -//! to the handler frame level, unwinding stops and the last personality routine -//! transfers control to its catch block. -//! -//! ## Frame unwind info registration -//! -//! Each module has its own frame unwind info section (usually ".eh_frame"), and -//! unwinder needs to know about all of them in order for unwinding to be able to -//! cross module boundaries. -//! -//! On some platforms, like Linux, this is achieved by dynamically enumerating -//! currently loaded modules via the dl_iterate_phdr() API and finding all -//! .eh_frame sections. -//! -//! Others, like Windows, require modules to actively register their unwind info -//! sections by calling __register_frame_info() API at startup. In the latter -//! case it is essential that there is only one copy of the unwinder runtime in -//! the process. This is usually achieved by linking to the dynamic version of -//! the unwind runtime. -//! -//! Currently Rust uses unwind runtime provided by libgcc. - -#![allow(dead_code)] -#![allow(unused_imports)] - -use prelude::v1::*; - -use any::Any; -use boxed; -use cell::Cell; -use cmp; -use panicking; -use fmt; -use intrinsics; -use mem; -use sync::atomic::{self, Ordering}; -use sys_common::mutex::Mutex; - -// The actual unwinding implementation is cfg'd here, and we've got two current -// implementations. One goes through SEH on Windows and the other goes through -// libgcc via the libunwind-like API. - -// i686-pc-windows-msvc -#[cfg(all(windows, target_arch = "x86", target_env = "msvc"))] -#[path = "seh.rs"] #[doc(hidden)] -pub mod imp; - -// x86_64-pc-windows-* -#[cfg(all(windows, target_arch = "x86_64"))] -#[path = "seh64_gnu.rs"] #[doc(hidden)] -pub mod imp; - -// i686-pc-windows-gnu and all others -#[cfg(any(unix, all(windows, target_arch = "x86", target_env = "gnu")))] -#[path = "gcc.rs"] #[doc(hidden)] -pub mod imp; - -pub type Callback = fn(msg: &(Any + Send), file: &'static str, line: u32); - -// Variables used for invoking callbacks when a thread starts to unwind. -// -// For more information, see below. -const MAX_CALLBACKS: usize = 16; -static CALLBACKS: [atomic::AtomicUsize; MAX_CALLBACKS] = - [atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), - atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), - atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), - atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), - atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), - atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), - atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), - atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0)]; -static CALLBACK_CNT: atomic::AtomicUsize = atomic::AtomicUsize::new(0); - -thread_local! { static PANICKING: Cell = Cell::new(false) } - -/// Invoke a closure, capturing the cause of panic if one occurs. -/// -/// This function will return `Ok(())` if the closure did not panic, and will -/// return `Err(cause)` if the closure panics. The `cause` returned is the -/// object with which panic was originally invoked. -/// -/// This function also is unsafe for a variety of reasons: -/// -/// * This is not safe to call in a nested fashion. The unwinding -/// interface for Rust is designed to have at most one try/catch block per -/// thread, not multiple. No runtime checking is currently performed to uphold -/// this invariant, so this function is not safe. A nested try/catch block -/// may result in corruption of the outer try/catch block's state, especially -/// if this is used within a thread itself. -/// -/// * It is not sound to trigger unwinding while already unwinding. Rust threads -/// have runtime checks in place to ensure this invariant, but it is not -/// guaranteed that a rust thread is in place when invoking this function. -/// Unwinding twice can lead to resource leaks where some destructors are not -/// run. -pub unsafe fn try(f: F) -> Result<(), Box> { - let mut f = Some(f); - return inner_try(try_fn::, &mut f as *mut _ as *mut u8); - - // If an inner function were not used here, then this generic function `try` - // uses the native symbol `rust_try`, for which the code is statically - // linked into the standard library. This means that the DLL for the - // standard library must have `rust_try` as an exposed symbol that - // downstream crates can link against (because monomorphizations of `try` in - // downstream crates will have a reference to the `rust_try` symbol). - // - // On MSVC this requires the symbol `rust_try` to be tagged with - // `dllexport`, but it's easier to not have conditional `src/rt/rust_try.ll` - // files and instead just have this non-generic shim the compiler can take - // care of exposing correctly. - unsafe fn inner_try(f: fn(*mut u8), data: *mut u8) - -> Result<(), Box> { - let prev = PANICKING.with(|s| s.get()); - PANICKING.with(|s| s.set(false)); - let ep = intrinsics::try(f, data); - PANICKING.with(|s| s.set(prev)); - if ep.is_null() { - Ok(()) - } else { - Err(imp::cleanup(ep)) - } - } - - fn try_fn(opt_closure: *mut u8) { - let opt_closure = opt_closure as *mut Option; - unsafe { (*opt_closure).take().unwrap()(); } - } - - extern { - // Rust's try-catch - // When f(...) returns normally, the return value is null. - // When f(...) throws, the return value is a pointer to the caught - // exception object. - fn rust_try(f: extern fn(*mut u8), - data: *mut u8) -> *mut u8; - } -} - -/// Determines whether the current thread is unwinding because of panic. -pub fn panicking() -> bool { - PANICKING.with(|s| s.get()) -} - -// An uninlined, unmangled function upon which to slap yer breakpoints -#[inline(never)] -#[no_mangle] -#[allow(private_no_mangle_fns)] -fn rust_panic(cause: Box) -> ! { - rtdebug!("begin_unwind()"); - unsafe { - imp::panic(cause) - } -} - -#[cfg(not(test))] -/// Entry point of panic from the libcore crate. -#[lang = "panic_fmt"] -pub extern fn rust_begin_unwind(msg: fmt::Arguments, - file: &'static str, line: u32) -> ! { - begin_unwind_fmt(msg, &(file, line)) -} - -/// The entry point for unwinding with a formatted message. -/// -/// This is designed to reduce the amount of code required at the call -/// site as much as possible (so that `panic!()` has as low an impact -/// on (e.g.) the inlining of other functions as possible), by moving -/// the actual formatting into this shared place. -#[inline(never)] #[cold] -pub fn begin_unwind_fmt(msg: fmt::Arguments, file_line: &(&'static str, u32)) -> ! { - use fmt::Write; - - // We do two allocations here, unfortunately. But (a) they're - // required with the current scheme, and (b) we don't handle - // panic + OOM properly anyway (see comment in begin_unwind - // below). - - let mut s = String::new(); - let _ = s.write_fmt(msg); - begin_unwind_inner(Box::new(s), file_line) -} - -/// This is the entry point of unwinding for panic!() and assert!(). -#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible -pub fn begin_unwind(msg: M, file_line: &(&'static str, u32)) -> ! { - // Note that this should be the only allocation performed in this code path. - // Currently this means that panic!() on OOM will invoke this code path, - // but then again we're not really ready for panic on OOM anyway. If - // we do start doing this, then we should propagate this allocation to - // be performed in the parent of this thread instead of the thread that's - // panicking. - - // see below for why we do the `Any` coercion here. - begin_unwind_inner(Box::new(msg), file_line) -} - -/// The core of the unwinding. -/// -/// This is non-generic to avoid instantiation bloat in other crates -/// (which makes compilation of small crates noticeably slower). (Note: -/// we need the `Any` object anyway, we're not just creating it to -/// avoid being generic.) -/// -/// Doing this split took the LLVM IR line counts of `fn main() { panic!() -/// }` from ~1900/3700 (-O/no opts) to 180/590. -#[inline(never)] #[cold] // this is the slow path, please never inline this -fn begin_unwind_inner(msg: Box, - file_line: &(&'static str, u32)) -> ! { - // Make sure the default failure handler is registered before we look at the - // callbacks. We also use a raw sys-based mutex here instead of a - // `std::sync` one as accessing TLS can cause weird recursive problems (and - // we don't need poison checking). - unsafe { - static LOCK: Mutex = Mutex::new(); - static mut INIT: bool = false; - LOCK.lock(); - if !INIT { - register(panicking::on_panic); - INIT = true; - } - LOCK.unlock(); - } - - // First, invoke call the user-defined callbacks triggered on thread panic. - // - // By the time that we see a callback has been registered (by reading - // MAX_CALLBACKS), the actual callback itself may have not been stored yet, - // so we just chalk it up to a race condition and move on to the next - // callback. Additionally, CALLBACK_CNT may briefly be higher than - // MAX_CALLBACKS, so we're sure to clamp it as necessary. - let callbacks = { - let amt = CALLBACK_CNT.load(Ordering::SeqCst); - &CALLBACKS[..cmp::min(amt, MAX_CALLBACKS)] - }; - for cb in callbacks { - match cb.load(Ordering::SeqCst) { - 0 => {} - n => { - let f: Callback = unsafe { mem::transmute(n) }; - let (file, line) = *file_line; - f(&*msg, file, line); - } - } - }; - - // Now that we've run all the necessary unwind callbacks, we actually - // perform the unwinding. - if panicking() { - // If a thread panics while it's already unwinding then we - // have limited options. Currently our preference is to - // just abort. In the future we may consider resuming - // unwinding or otherwise exiting the thread cleanly. - rterrln!("thread panicked while panicking. aborting."); - unsafe { intrinsics::abort() } - } - PANICKING.with(|s| s.set(true)); - rust_panic(msg); -} - -/// Register a callback to be invoked when a thread unwinds. -/// -/// This is an unsafe and experimental API which allows for an arbitrary -/// callback to be invoked when a thread panics. This callback is invoked on both -/// the initial unwinding and a double unwinding if one occurs. Additionally, -/// the local `Thread` will be in place for the duration of the callback, and -/// the callback must ensure that it remains in place once the callback returns. -/// -/// Only a limited number of callbacks can be registered, and this function -/// returns whether the callback was successfully registered or not. It is not -/// currently possible to unregister a callback once it has been registered. -pub unsafe fn register(f: Callback) -> bool { - match CALLBACK_CNT.fetch_add(1, Ordering::SeqCst) { - // The invocation code has knowledge of this window where the count has - // been incremented, but the callback has not been stored. We're - // guaranteed that the slot we're storing into is 0. - n if n < MAX_CALLBACKS => { - let prev = CALLBACKS[n].swap(mem::transmute(f), Ordering::SeqCst); - rtassert!(prev == 0); - true - } - // If we accidentally bumped the count too high, pull it back. - _ => { - CALLBACK_CNT.store(MAX_CALLBACKS, Ordering::SeqCst); - false - } - } -} diff --git a/src/libstd/rt/unwind/seh.rs b/src/libstd/rt/unwind/seh.rs deleted file mode 100644 index 8c793758166..00000000000 --- a/src/libstd/rt/unwind/seh.rs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx) -//! -//! On Windows (currently only on MSVC), the default exception handling -//! mechanism is Structured Exception Handling (SEH). This is quite different -//! than Dwarf-based exception handling (e.g. what other unix platforms use) in -//! terms of compiler internals, so LLVM is required to have a good deal of -//! extra support for SEH. Currently this support is somewhat lacking, so what's -//! here is the bare bones of SEH support. -//! -//! In a nutshell, what happens here is: -//! -//! 1. The `panic` function calls the standard Windows function `RaiseException` -//! with a Rust-specific code, triggering the unwinding process. -//! 2. All landing pads generated by the compiler (just "cleanup" landing pads) -//! use the personality function `__C_specific_handler`, a function in the -//! CRT, and the unwinding code in Windows will use this personality function -//! to execute all cleanup code on the stack. -//! 3. Eventually the "catch" code in `rust_try` (located in -//! src/rt/rust_try_msvc_64.ll) is executed, which will ensure that the -//! exception being caught is indeed a Rust exception, returning control back -//! into Rust. -//! -//! Some specific differences from the gcc-based exception handling are: -//! -//! * Rust has no custom personality function, it is instead *always* -//! __C_specific_handler, so the filtering is done in a C++-like manner -//! instead of in the personality function itself. Note that the specific -//! syntax for this (found in the rust_try_msvc_64.ll) is taken from an LLVM -//! test case for SEH. -//! * We've got some data to transmit across the unwinding boundary, -//! specifically a `Box`. In Dwarf-based unwinding this -//! data is part of the payload of the exception, but I have not currently -//! figured out how to do this with LLVM's bindings. Judging by some comments -//! in the LLVM test cases this may not even be possible currently with LLVM, -//! so this is just abandoned entirely. Instead the data is stored in a -//! thread-local in `panic` and retrieved during `cleanup`. -//! -//! So given all that, the bindings here are pretty small, - -#![allow(bad_style)] - -use prelude::v1::*; - -use any::Any; -use libc::{c_ulong, DWORD, c_void}; -use ptr; -use sys_common::thread_local::StaticKey; - -// 0x R U S T -const RUST_PANIC: DWORD = 0x52555354; -static PANIC_DATA: StaticKey = StaticKey::new(None); - -// This function is provided by kernel32.dll -extern "system" { - fn RaiseException(dwExceptionCode: DWORD, - dwExceptionFlags: DWORD, - nNumberOfArguments: DWORD, - lpArguments: *const c_ulong); -} - -#[repr(C)] -pub struct EXCEPTION_POINTERS { - ExceptionRecord: *mut EXCEPTION_RECORD, - ContextRecord: *mut CONTEXT, -} - -enum CONTEXT {} - -#[repr(C)] -struct EXCEPTION_RECORD { - ExceptionCode: DWORD, - ExceptionFlags: DWORD, - ExceptionRecord: *mut _EXCEPTION_RECORD, - ExceptionAddress: *mut c_void, - NumberParameters: DWORD, - ExceptionInformation: [*mut c_ulong; EXCEPTION_MAXIMUM_PARAMETERS], -} - -enum _EXCEPTION_RECORD {} - -const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15; - -pub unsafe fn panic(data: Box) -> ! { - // See module docs above for an explanation of why `data` is stored in a - // thread local instead of being passed as an argument to the - // `RaiseException` function (which can in theory carry along arbitrary - // data). - let exception = Box::new(data); - rtassert!(PANIC_DATA.get().is_null()); - PANIC_DATA.set(Box::into_raw(exception) as *mut u8); - - RaiseException(RUST_PANIC, 0, 0, ptr::null()); - rtabort!("could not unwind stack"); -} - -pub unsafe fn cleanup(ptr: *mut u8) -> Box { - // The `ptr` here actually corresponds to the code of the exception, and our - // real data is stored in our thread local. - rtassert!(ptr as DWORD == RUST_PANIC); - - let data = PANIC_DATA.get() as *mut Box; - PANIC_DATA.set(ptr::null_mut()); - rtassert!(!data.is_null()); - - *Box::from_raw(data) -} - -// This is required by the compiler to exist (e.g. it's a lang item), but it's -// never actually called by the compiler because __C_specific_handler is the -// personality function that is always used. Hence this is just an aborting -// stub. -#[lang = "eh_personality"] -fn rust_eh_personality() { - unsafe { ::intrinsics::abort() } -} - -// This is a function referenced from `rust_try_msvc_64.ll` which is used to -// filter the exceptions being caught by that function. -// -// In theory local variables can be accessed through the `rbp` parameter of this -// function, but a comment in an LLVM test case indicates that this is not -// implemented in LLVM, so this is just an idempotent function which doesn't -// ferry along any other information. -// -// This function just takes a look at the current EXCEPTION_RECORD being thrown -// to ensure that it's code is RUST_PANIC, which was set by the call to -// `RaiseException` above in the `panic` function. -#[no_mangle] -#[lang = "msvc_try_filter"] -pub extern fn __rust_try_filter(eh_ptrs: *mut EXCEPTION_POINTERS, - _rbp: *mut u8) -> i32 { - unsafe { - ((*(*eh_ptrs).ExceptionRecord).ExceptionCode == RUST_PANIC) as i32 - } -} diff --git a/src/libstd/rt/unwind/seh64_gnu.rs b/src/libstd/rt/unwind/seh64_gnu.rs deleted file mode 100644 index 78f969bfbeb..00000000000 --- a/src/libstd/rt/unwind/seh64_gnu.rs +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Unwinding implementation of top of native Win64 SEH, -//! however the unwind handler data (aka LSDA) uses GCC-compatible encoding. - -#![allow(bad_style)] -#![allow(private_no_mangle_fns)] - -use prelude::v1::*; - -use any::Any; -use self::EXCEPTION_DISPOSITION::*; -use rt::dwarf::eh; -use core::mem; -use core::ptr; -use libc::{c_void, c_ulonglong, DWORD, LPVOID}; -type ULONG_PTR = c_ulonglong; - -// Define our exception codes: -// according to http://msdn.microsoft.com/en-us/library/het71c37(v=VS.80).aspx, -// [31:30] = 3 (error), 2 (warning), 1 (info), 0 (success) -// [29] = 1 (user-defined) -// [28] = 0 (reserved) -// we define bits: -// [24:27] = type -// [0:23] = magic -const ETYPE: DWORD = 0b1110_u32 << 28; -const MAGIC: DWORD = 0x525354; // "RST" - -const RUST_PANIC: DWORD = ETYPE | (1 << 24) | MAGIC; - -const EXCEPTION_NONCONTINUABLE: DWORD = 0x1; // Noncontinuable exception -const EXCEPTION_UNWINDING: DWORD = 0x2; // Unwind is in progress -const EXCEPTION_EXIT_UNWIND: DWORD = 0x4; // Exit unwind is in progress -const EXCEPTION_STACK_INVALID: DWORD = 0x8; // Stack out of limits or unaligned -const EXCEPTION_NESTED_CALL: DWORD = 0x10; // Nested exception handler call -const EXCEPTION_TARGET_UNWIND: DWORD = 0x20; // Target unwind in progress -const EXCEPTION_COLLIDED_UNWIND: DWORD = 0x40; // Collided exception handler call -const EXCEPTION_UNWIND: DWORD = EXCEPTION_UNWINDING | - EXCEPTION_EXIT_UNWIND | - EXCEPTION_TARGET_UNWIND | - EXCEPTION_COLLIDED_UNWIND; - -#[repr(C)] -pub struct EXCEPTION_RECORD { - ExceptionCode: DWORD, - ExceptionFlags: DWORD, - ExceptionRecord: *const EXCEPTION_RECORD, - ExceptionAddress: LPVOID, - NumberParameters: DWORD, - ExceptionInformation: [ULONG_PTR; 15], -} - -pub enum CONTEXT {} -pub enum UNWIND_HISTORY_TABLE {} - -#[repr(C)] -pub struct RUNTIME_FUNCTION { - BeginAddress: DWORD, - EndAddress: DWORD, - UnwindData: DWORD, -} - -#[repr(C)] -pub struct DISPATCHER_CONTEXT { - ControlPc: LPVOID, - ImageBase: LPVOID, - FunctionEntry: *const RUNTIME_FUNCTION, - EstablisherFrame: LPVOID, - TargetIp: LPVOID, - ContextRecord: *const CONTEXT, - LanguageHandler: LPVOID, - HandlerData: *const u8, - HistoryTable: *const UNWIND_HISTORY_TABLE, -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub enum EXCEPTION_DISPOSITION { - ExceptionContinueExecution, - ExceptionContinueSearch, - ExceptionNestedException, - ExceptionCollidedUnwind -} - -// From kernel32.dll -extern "system" { - fn RaiseException(dwExceptionCode: DWORD, - dwExceptionFlags: DWORD, - nNumberOfArguments: DWORD, - lpArguments: *const ULONG_PTR); - - fn RtlUnwindEx(TargetFrame: LPVOID, - TargetIp: LPVOID, - ExceptionRecord: *const EXCEPTION_RECORD, - ReturnValue: LPVOID, - OriginalContext: *const CONTEXT, - HistoryTable: *const UNWIND_HISTORY_TABLE); -} - -#[repr(C)] -struct PanicData { - data: Box -} - -pub unsafe fn panic(data: Box) -> ! { - let panic_ctx = Box::new(PanicData { data: data }); - let params = [Box::into_raw(panic_ctx) as ULONG_PTR]; - rtdebug!("panic: ctx={:X}", params[0]); - RaiseException(RUST_PANIC, - EXCEPTION_NONCONTINUABLE, - params.len() as DWORD, - ¶ms as *const ULONG_PTR); - rtabort!("could not unwind stack"); -} - -pub unsafe fn cleanup(ptr: *mut u8) -> Box { - rtdebug!("cleanup: ctx={:X}", ptr as usize); - let panic_ctx = Box::from_raw(ptr as *mut PanicData); - return panic_ctx.data; -} - -// SEH doesn't support resuming unwinds after calling a landing pad like -// libunwind does. For this reason, MSVC compiler outlines landing pads into -// separate functions that can be called directly from the personality function -// but are nevertheless able to find and modify stack frame of the "parent" -// function. -// -// Since this cannot be done with libdwarf-style landing pads, -// rust_eh_personality instead catches RUST_PANICs, runs the landing pad, then -// reraises the exception. -// -// Note that it makes certain assumptions about the exception: -// -// 1. That RUST_PANIC is non-continuable, so no lower stack frame may choose to -// resume execution. -// 2. That the first parameter of the exception is a pointer to an extra data -// area (PanicData). -// Since these assumptions do not generally hold true for foreign exceptions -// (system faults, C++ exceptions, etc), we make no attempt to invoke our -// landing pads (and, thus, destructors!) for anything other than RUST_PANICs. -// This is considered acceptable, because the behavior of throwing exceptions -// through a C ABI boundary is undefined. - -#[lang = "eh_personality_catch"] -#[cfg(not(test))] -unsafe extern fn rust_eh_personality_catch( - exceptionRecord: *mut EXCEPTION_RECORD, - establisherFrame: LPVOID, - contextRecord: *mut CONTEXT, - dispatcherContext: *mut DISPATCHER_CONTEXT -) -> EXCEPTION_DISPOSITION -{ - rust_eh_personality(exceptionRecord, establisherFrame, - contextRecord, dispatcherContext) -} - -#[lang = "eh_personality"] -#[cfg(not(test))] -unsafe extern fn rust_eh_personality( - exceptionRecord: *mut EXCEPTION_RECORD, - establisherFrame: LPVOID, - contextRecord: *mut CONTEXT, - dispatcherContext: *mut DISPATCHER_CONTEXT -) -> EXCEPTION_DISPOSITION -{ - let er = &*exceptionRecord; - let dc = &*dispatcherContext; - rtdebug!("rust_eh_personality: code={:X}, flags={:X}, frame={:X}, ip={:X}", - er.ExceptionCode, er.ExceptionFlags, - establisherFrame as usize, dc.ControlPc as usize); - - if er.ExceptionFlags & EXCEPTION_UNWIND == 0 { // we are in the dispatch phase - if er.ExceptionCode == RUST_PANIC { - if let Some(lpad) = find_landing_pad(dc) { - rtdebug!("unwinding to landing pad {:X}", lpad); - - RtlUnwindEx(establisherFrame, - lpad as LPVOID, - exceptionRecord, - er.ExceptionInformation[0] as LPVOID, // pointer to PanicData - contextRecord, - dc.HistoryTable); - rtabort!("could not unwind"); - } - } - } - ExceptionContinueSearch -} - -// The `resume` instruction, found at the end of the landing pads, and whose job -// is to resume stack unwinding, is typically lowered by LLVM into a call to -// `_Unwind_Resume` routine. To avoid confusion with the same symbol exported -// from libgcc, we redirect it to `rust_eh_unwind_resume`. -// Since resolution of this symbol is done by the linker, `rust_eh_unwind_resume` -// must be marked `pub` + `#[no_mangle]`. (Can we make it a lang item?) - -#[lang = "eh_unwind_resume"] -#[cfg(not(test))] -unsafe extern fn rust_eh_unwind_resume(panic_ctx: LPVOID) { - rtdebug!("rust_eh_unwind_resume: ctx={:X}", panic_ctx as usize); - let params = [panic_ctx as ULONG_PTR]; - RaiseException(RUST_PANIC, - EXCEPTION_NONCONTINUABLE, - params.len() as DWORD, - ¶ms as *const ULONG_PTR); - rtabort!("could not resume unwind"); -} - -unsafe fn find_landing_pad(dc: &DISPATCHER_CONTEXT) -> Option { - let eh_ctx = eh::EHContext { - ip: dc.ControlPc as usize, - func_start: dc.ImageBase as usize + (*dc.FunctionEntry).BeginAddress as usize, - text_start: dc.ImageBase as usize, - data_start: 0 - }; - eh::find_landing_pad(dc.HandlerData, &eh_ctx) -} diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs deleted file mode 100644 index 23a3c3e38c4..00000000000 --- a/src/libstd/rt/util.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use io::prelude::*; - -use env; -use fmt; -use intrinsics; -use sync::atomic::{self, Ordering}; -use sys::stdio::Stderr; - -pub fn min_stack() -> usize { - static MIN: atomic::AtomicUsize = atomic::AtomicUsize::new(0); - match MIN.load(Ordering::SeqCst) { - 0 => {} - n => return n - 1, - } - let amt = env::var("RUST_MIN_STACK").ok().and_then(|s| s.parse().ok()); - let amt = amt.unwrap_or(2 * 1024 * 1024); - // 0 is our sentinel value, so ensure that we'll never see 0 after - // initialization has run - MIN.store(amt + 1, Ordering::SeqCst); - amt -} - -// Indicates whether we should perform expensive sanity checks, including rtassert! -// -// FIXME: Once the runtime matures remove the `true` below to turn off rtassert, -// etc. -pub const ENFORCE_SANITY: bool = true || !cfg!(rtopt) || cfg!(rtdebug) || - cfg!(rtassert); - -pub fn dumb_print(args: fmt::Arguments) { - let _ = Stderr::new().map(|mut stderr| stderr.write_fmt(args)); -} - -pub fn abort(args: fmt::Arguments) -> ! { - rterrln!("fatal runtime error: {}", args); - unsafe { intrinsics::abort(); } -} - -pub unsafe fn report_overflow() { - use thread; - rterrln!("\nthread '{}' has overflowed its stack", - thread::current().name().unwrap_or("")); -} diff --git a/src/libstd/sys/common/args.rs b/src/libstd/sys/common/args.rs new file mode 100644 index 00000000000..4cfddb036e9 --- /dev/null +++ b/src/libstd/sys/common/args.rs @@ -0,0 +1,99 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Global storage for command line arguments +//! +//! The current incarnation of the Rust runtime expects for +//! the processes `argc` and `argv` arguments to be stored +//! in a globally-accessible location for use by the `os` module. +//! +//! Only valid to call on Linux. Mac and Windows use syscalls to +//! discover the command line arguments. +//! +//! FIXME #7756: Would be nice for this to not exist. + +#![allow(dead_code)] // different code on OSX/linux/etc + +use vec::Vec; + +/// One-time global initialization. +pub unsafe fn init(argc: isize, argv: *const *const u8) { imp::init(argc, argv) } + +/// One-time global cleanup. +pub unsafe fn cleanup() { imp::cleanup() } + +/// Make a clone of the global arguments. +pub fn clone() -> Option>> { imp::clone() } + +#[cfg(any(target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "bitrig", + target_os = "netbsd", + target_os = "openbsd"))] +mod imp { + use prelude::v1::*; + + use libc::c_char; + use mem; + use ffi::CStr; + + use sync::StaticMutex; + + static mut GLOBAL_ARGS_PTR: usize = 0; + static LOCK: StaticMutex = StaticMutex::new(); + + pub unsafe fn init(argc: isize, argv: *const *const u8) { + let args = (0..argc).map(|i| { + CStr::from_ptr(*argv.offset(i) as *const c_char).to_bytes().to_vec() + }).collect(); + + let _guard = LOCK.lock(); + let ptr = get_global_ptr(); + assert!((*ptr).is_none()); + (*ptr) = Some(box args); + } + + pub unsafe fn cleanup() { + let _guard = LOCK.lock(); + *get_global_ptr() = None; + } + + pub fn clone() -> Option>> { + let _guard = LOCK.lock(); + unsafe { + let ptr = get_global_ptr(); + (*ptr).as_ref().map(|s| (**s).clone()) + } + } + + fn get_global_ptr() -> *mut Option>>> { + unsafe { mem::transmute(&GLOBAL_ARGS_PTR) } + } + +} + +#[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "windows"))] +mod imp { + use vec::Vec; + + pub unsafe fn init(_argc: isize, _argv: *const *const u8) { + } + + pub fn cleanup() { + } + + pub fn clone() -> Option>> { + panic!() + } +} diff --git a/src/libstd/sys/common/at_exit_imp.rs b/src/libstd/sys/common/at_exit_imp.rs new file mode 100644 index 00000000000..b2683750d67 --- /dev/null +++ b/src/libstd/sys/common/at_exit_imp.rs @@ -0,0 +1,82 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation of running at_exit routines +//! +//! Documentation can be found on the `rt::at_exit` function. + +use alloc::boxed::FnBox; +use boxed::Box; +use ptr; +use sys_common::mutex::Mutex; +use vec::Vec; + +type Queue = Vec>; + +// NB these are specifically not types from `std::sync` as they currently rely +// on poisoning and this module needs to operate at a lower level than requiring +// the thread infrastructure to be in place (useful on the borders of +// initialization/destruction). +static LOCK: Mutex = Mutex::new(); +static mut QUEUE: *mut Queue = ptr::null_mut(); + +// The maximum number of times the cleanup routines will be run. While running +// the at_exit closures new ones may be registered, and this count is the number +// of times the new closures will be allowed to register successfully. After +// this number of iterations all new registrations will return `false`. +const ITERS: usize = 10; + +unsafe fn init() -> bool { + if QUEUE.is_null() { + let state: Box = box Vec::new(); + QUEUE = Box::into_raw(state); + } else if QUEUE as usize == 1 { + // can't re-init after a cleanup + return false + } + + true +} + +pub fn cleanup() { + for i in 0..ITERS { + unsafe { + LOCK.lock(); + let queue = QUEUE; + QUEUE = if i == ITERS - 1 {1} else {0} as *mut _; + LOCK.unlock(); + + // make sure we're not recursively cleaning up + assert!(queue as usize != 1); + + // If we never called init, not need to cleanup! + if queue as usize != 0 { + let queue: Box = Box::from_raw(queue); + for to_run in *queue { + to_run(); + } + } + } + } +} + +pub fn push(f: Box) -> bool { + let mut ret = true; + unsafe { + LOCK.lock(); + if init() { + (*QUEUE).push(f); + } else { + ret = false; + } + LOCK.unlock(); + } + ret +} diff --git a/src/libstd/sys/common/backtrace.rs b/src/libstd/sys/common/backtrace.rs index 7845714e7f6..e7bda9a7ba2 100644 --- a/src/libstd/sys/common/backtrace.rs +++ b/src/libstd/sys/common/backtrace.rs @@ -8,10 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use io; +use env; use io::prelude::*; -use str; +use io; use libc; +use str; +use sync::atomic::{self, Ordering}; + +pub use sys::backtrace::write; #[cfg(target_pointer_width = "64")] pub const HEX_WIDTH: usize = 18; @@ -19,6 +23,23 @@ pub const HEX_WIDTH: usize = 18; #[cfg(target_pointer_width = "32")] pub const HEX_WIDTH: usize = 10; +// For now logging is turned off by default, and this function checks to see +// whether the magical environment variable is present to see if it's turned on. +pub fn log_enabled() -> bool { + static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0); + match ENABLED.load(Ordering::SeqCst) { + 1 => return false, + 2 => return true, + _ => {} + } + + let val = match env::var_os("RUST_BACKTRACE") { + Some(..) => 2, + None => 1, + }; + ENABLED.store(val, Ordering::SeqCst); + val == 2 +} // These output functions should now be used everywhere to ensure consistency. pub fn output(w: &mut Write, idx: isize, addr: *mut libc::c_void, @@ -163,3 +184,43 @@ pub fn demangle(writer: &mut Write, s: &str) -> io::Result<()> { Ok(()) } + +#[cfg(test)] +mod tests { + use prelude::v1::*; + use sys_common; + macro_rules! t { ($a:expr, $b:expr) => ({ + let mut m = Vec::new(); + sys_common::backtrace::demangle(&mut m, $a).unwrap(); + assert_eq!(String::from_utf8(m).unwrap(), $b); + }) } + + #[test] + fn demangle() { + t!("test", "test"); + t!("_ZN4testE", "test"); + t!("_ZN4test", "_ZN4test"); + t!("_ZN4test1a2bcE", "test::a::bc"); + } + + #[test] + fn demangle_dollars() { + t!("_ZN4$RP$E", ")"); + t!("_ZN8$RF$testE", "&test"); + t!("_ZN8$BP$test4foobE", "*test::foob"); + t!("_ZN9$u20$test4foobE", " test::foob"); + } + + #[test] + fn demangle_many_dollars() { + t!("_ZN13test$u20$test4foobE", "test test::foob"); + t!("_ZN12test$BP$test4foobE", "test*test::foob"); + } + + #[test] + fn demangle_windows() { + t!("ZN4testE", "test"); + t!("ZN13test$u20$test4foobE", "test test::foob"); + t!("ZN12test$RF$test4foobE", "test&test::foob"); + } +} diff --git a/src/libstd/sys/common/dwarf/eh.rs b/src/libstd/sys/common/dwarf/eh.rs new file mode 100644 index 00000000000..319be245bde --- /dev/null +++ b/src/libstd/sys/common/dwarf/eh.rs @@ -0,0 +1,159 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Parsing of GCC-style Language-Specific Data Area (LSDA) +//! For details see: +//! http://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html +//! http://mentorembedded.github.io/cxx-abi/exceptions.pdf +//! http://www.airs.com/blog/archives/460 +//! http://www.airs.com/blog/archives/464 +//! +//! A reference implementation may be found in the GCC source tree +//! (/libgcc/unwind-c.c as of this writing) + +#![allow(non_upper_case_globals)] +#![allow(unused)] + +use prelude::v1::*; +use sys_common::dwarf::DwarfReader; +use core::mem; + +pub const DW_EH_PE_omit : u8 = 0xFF; +pub const DW_EH_PE_absptr : u8 = 0x00; + +pub const DW_EH_PE_uleb128 : u8 = 0x01; +pub const DW_EH_PE_udata2 : u8 = 0x02; +pub const DW_EH_PE_udata4 : u8 = 0x03; +pub const DW_EH_PE_udata8 : u8 = 0x04; +pub const DW_EH_PE_sleb128 : u8 = 0x09; +pub const DW_EH_PE_sdata2 : u8 = 0x0A; +pub const DW_EH_PE_sdata4 : u8 = 0x0B; +pub const DW_EH_PE_sdata8 : u8 = 0x0C; + +pub const DW_EH_PE_pcrel : u8 = 0x10; +pub const DW_EH_PE_textrel : u8 = 0x20; +pub const DW_EH_PE_datarel : u8 = 0x30; +pub const DW_EH_PE_funcrel : u8 = 0x40; +pub const DW_EH_PE_aligned : u8 = 0x50; + +pub const DW_EH_PE_indirect : u8 = 0x80; + +#[derive(Copy, Clone)] +pub struct EHContext { + pub ip: usize, // Current instruction pointer + pub func_start: usize, // Address of the current function + pub text_start: usize, // Address of the code section + pub data_start: usize, // Address of the data section +} + +pub unsafe fn find_landing_pad(lsda: *const u8, context: &EHContext) + -> Option { + if lsda.is_null() { + return None; + } + + let func_start = context.func_start; + let mut reader = DwarfReader::new(lsda); + + let start_encoding = reader.read::(); + // base address for landing pad offsets + let lpad_base = if start_encoding != DW_EH_PE_omit { + read_encoded_pointer(&mut reader, context, start_encoding) + } else { + func_start + }; + + let ttype_encoding = reader.read::(); + if ttype_encoding != DW_EH_PE_omit { + // Rust doesn't analyze exception types, so we don't care about the type table + reader.read_uleb128(); + } + + let call_site_encoding = reader.read::(); + let call_site_table_length = reader.read_uleb128(); + let action_table = reader.ptr.offset(call_site_table_length as isize); + // Return addresses point 1 byte past the call instruction, which could + // be in the next IP range. + let ip = context.ip-1; + + while reader.ptr < action_table { + let cs_start = read_encoded_pointer(&mut reader, context, call_site_encoding); + let cs_len = read_encoded_pointer(&mut reader, context, call_site_encoding); + let cs_lpad = read_encoded_pointer(&mut reader, context, call_site_encoding); + let cs_action = reader.read_uleb128(); + // Callsite table is sorted by cs_start, so if we've passed the ip, we + // may stop searching. + if ip < func_start + cs_start { + break + } + if ip < func_start + cs_start + cs_len { + if cs_lpad != 0 { + return Some(lpad_base + cs_lpad); + } else { + return None; + } + } + } + // IP range not found: gcc's C++ personality calls terminate() here, + // however the rest of the languages treat this the same as cs_lpad == 0. + // We follow this suit. + None +} + +#[inline] +fn round_up(unrounded: usize, align: usize) -> usize { + assert!(align.is_power_of_two()); + (unrounded + align - 1) & !(align - 1) +} + +unsafe fn read_encoded_pointer(reader: &mut DwarfReader, + context: &EHContext, + encoding: u8) -> usize { + assert!(encoding != DW_EH_PE_omit); + + // DW_EH_PE_aligned implies it's an absolute pointer value + if encoding == DW_EH_PE_aligned { + reader.ptr = round_up(reader.ptr as usize, + mem::size_of::()) as *const u8; + return reader.read::(); + } + + let mut result = match encoding & 0x0F { + DW_EH_PE_absptr => reader.read::(), + DW_EH_PE_uleb128 => reader.read_uleb128() as usize, + DW_EH_PE_udata2 => reader.read::() as usize, + DW_EH_PE_udata4 => reader.read::() as usize, + DW_EH_PE_udata8 => reader.read::() as usize, + DW_EH_PE_sleb128 => reader.read_sleb128() as usize, + DW_EH_PE_sdata2 => reader.read::() as usize, + DW_EH_PE_sdata4 => reader.read::() as usize, + DW_EH_PE_sdata8 => reader.read::() as usize, + _ => panic!() + }; + + result += match encoding & 0x70 { + DW_EH_PE_absptr => 0, + // relative to address of the encoded value, despite the name + DW_EH_PE_pcrel => reader.ptr as usize, + DW_EH_PE_textrel => { assert!(context.text_start != 0); + context.text_start }, + DW_EH_PE_datarel => { assert!(context.data_start != 0); + context.data_start }, + DW_EH_PE_funcrel => { assert!(context.func_start != 0); + context.func_start }, + _ => panic!() + }; + + if encoding & DW_EH_PE_indirect != 0 { + result = *(result as *const usize); + } + + result +} diff --git a/src/libstd/sys/common/dwarf/mod.rs b/src/libstd/sys/common/dwarf/mod.rs new file mode 100644 index 00000000000..822826bcc83 --- /dev/null +++ b/src/libstd/sys/common/dwarf/mod.rs @@ -0,0 +1,107 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Utilities for parsing DWARF-encoded data streams. +//! See http://www.dwarfstd.org, +//! DWARF-4 standard, Section 7 - "Data Representation" + +// This module is used only by x86_64-pc-windows-gnu for now, but we +// are compiling it everywhere to avoid regressions. +#![allow(unused)] + +pub mod eh; + +use prelude::v1::*; +use core::mem; + +pub struct DwarfReader { + pub ptr : *const u8 +} + +#[repr(C,packed)] +struct Unaligned(T); + +impl DwarfReader { + + pub fn new(ptr : *const u8) -> DwarfReader { + DwarfReader { + ptr : ptr + } + } + + // DWARF streams are packed, so e.g. a u32 would not necessarily be aligned + // on a 4-byte boundary. This may cause problems on platforms with strict + // alignment requirements. By wrapping data in a "packed" struct, we are + // telling the backend to generate "misalignment-safe" code. + pub unsafe fn read(&mut self) -> T { + let Unaligned(result) = *(self.ptr as *const Unaligned); + self.ptr = self.ptr.offset(mem::size_of::() as isize); + result + } + + // ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable + // Length Data". + pub unsafe fn read_uleb128(&mut self) -> u64 { + let mut shift : usize = 0; + let mut result : u64 = 0; + let mut byte : u8; + loop { + byte = self.read::(); + result |= ((byte & 0x7F) as u64) << shift; + shift += 7; + if byte & 0x80 == 0 { + break; + } + } + result + } + + pub unsafe fn read_sleb128(&mut self) -> i64 { + let mut shift : usize = 0; + let mut result : u64 = 0; + let mut byte : u8; + loop { + byte = self.read::(); + result |= ((byte & 0x7F) as u64) << shift; + shift += 7; + if byte & 0x80 == 0 { + break; + } + } + // sign-extend + if shift < 8 * mem::size_of::() && (byte & 0x40) != 0 { + result |= (!0 as u64) << shift; + } + result as i64 + } +} + +#[test] +fn dwarf_reader() { + let encoded: &[u8] = &[1, + 2, 3, + 4, 5, 6, 7, + 0xE5, 0x8E, 0x26, + 0x9B, 0xF1, 0x59, + 0xFF, 0xFF]; + + let mut reader = DwarfReader::new(encoded.as_ptr()); + + unsafe { + assert!(reader.read::() == u8::to_be(1u8)); + assert!(reader.read::() == u16::to_be(0x0203)); + assert!(reader.read::() == u32::to_be(0x04050607)); + + assert!(reader.read_uleb128() == 624485); + assert!(reader.read_sleb128() == -624485); + + assert!(reader.read::() == i8::to_be(-1)); + } +} diff --git a/src/libstd/sys/common/libunwind.rs b/src/libstd/sys/common/libunwind.rs new file mode 100644 index 00000000000..fde612014e9 --- /dev/null +++ b/src/libstd/sys/common/libunwind.rs @@ -0,0 +1,145 @@ +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Unwind library interface + +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(dead_code)] // these are just bindings + +#[cfg(any(not(target_arch = "arm"), target_os = "ios"))] +pub use self::_Unwind_Action::*; +#[cfg(target_arch = "arm")] +pub use self::_Unwind_State::*; +pub use self::_Unwind_Reason_Code::*; + +use libc; + +#[cfg(any(not(target_arch = "arm"), target_os = "ios"))] +#[repr(C)] +#[derive(Copy, Clone)] +pub enum _Unwind_Action { + _UA_SEARCH_PHASE = 1, + _UA_CLEANUP_PHASE = 2, + _UA_HANDLER_FRAME = 4, + _UA_FORCE_UNWIND = 8, + _UA_END_OF_STACK = 16, +} + +#[cfg(target_arch = "arm")] +#[repr(C)] +#[derive(Copy, Clone)] +pub enum _Unwind_State { + _US_VIRTUAL_UNWIND_FRAME = 0, + _US_UNWIND_FRAME_STARTING = 1, + _US_UNWIND_FRAME_RESUME = 2, + _US_ACTION_MASK = 3, + _US_FORCE_UNWIND = 8, + _US_END_OF_STACK = 16 +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub enum _Unwind_Reason_Code { + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8, + _URC_FAILURE = 9, // used only by ARM EABI +} + +pub type _Unwind_Exception_Class = u64; + +pub type _Unwind_Word = libc::uintptr_t; + +#[cfg(target_arch = "x86")] +pub const unwinder_private_data_size: usize = 5; + +#[cfg(target_arch = "x86_64")] +pub const unwinder_private_data_size: usize = 6; + +#[cfg(all(target_arch = "arm", not(target_os = "ios")))] +pub const unwinder_private_data_size: usize = 20; + +#[cfg(all(target_arch = "arm", target_os = "ios"))] +pub const unwinder_private_data_size: usize = 5; + +#[cfg(target_arch = "aarch64")] +pub const unwinder_private_data_size: usize = 2; + +#[cfg(any(target_arch = "mips", target_arch = "mipsel"))] +pub const unwinder_private_data_size: usize = 2; + +#[cfg(target_arch = "powerpc")] +pub const unwinder_private_data_size: usize = 2; + +#[repr(C)] +pub struct _Unwind_Exception { + pub exception_class: _Unwind_Exception_Class, + pub exception_cleanup: _Unwind_Exception_Cleanup_Fn, + pub private: [_Unwind_Word; unwinder_private_data_size], +} + +pub enum _Unwind_Context {} + +pub type _Unwind_Exception_Cleanup_Fn = + extern "C" fn(unwind_code: _Unwind_Reason_Code, + exception: *mut _Unwind_Exception); + +#[cfg(any(all(target_os = "linux", not(target_env = "musl")), + target_os = "freebsd"))] +#[link(name = "gcc_s")] +extern {} + +#[cfg(all(target_os = "linux", target_env = "musl", not(test)))] +#[link(name = "unwind", kind = "static")] +extern {} + +#[cfg(any(target_os = "android", target_os = "netbsd", target_os = "openbsd"))] +#[link(name = "gcc")] +extern {} + +#[cfg(target_os = "dragonfly")] +#[link(name = "gcc_pic")] +extern {} + +#[cfg(target_os = "bitrig")] +#[link(name = "c++abi")] +extern {} + +extern "C" { + // iOS on armv7 uses SjLj exceptions and requires to link + // against corresponding routine (..._SjLj_...) + #[cfg(not(all(target_os = "ios", target_arch = "arm")))] + pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) + -> _Unwind_Reason_Code; + + #[cfg(all(target_os = "ios", target_arch = "arm"))] + fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) + -> _Unwind_Reason_Code; + + pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception); +} + +// ... and now we just providing access to SjLj counterspart +// through a standard name to hide those details from others +// (see also comment above regarding _Unwind_RaiseException) +#[cfg(all(target_os = "ios", target_arch = "arm"))] +#[inline(always)] +pub unsafe fn _Unwind_RaiseException(exc: *mut _Unwind_Exception) + -> _Unwind_Reason_Code { + _Unwind_SjLj_RaiseException(exc) +} diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs index b8074235fb2..44c55d1e2c4 100644 --- a/src/libstd/sys/common/mod.rs +++ b/src/libstd/sys/common/mod.rs @@ -10,17 +10,39 @@ #![allow(missing_docs)] +use boxed::Box; +use sync::Once; +use sys; + +macro_rules! rtabort { + ($($t:tt)*) => (::sys_common::util::abort(format_args!($($t)*))) +} + +macro_rules! rtassert { + ($e:expr) => ({ + if !$e { + rtabort!(concat!("assertion failed: ", stringify!($e))) + } + }) +} + +pub mod args; +pub mod at_exit_imp; pub mod backtrace; pub mod condvar; +pub mod dwarf; +pub mod io; +pub mod libunwind; pub mod mutex; pub mod net; -pub mod io; pub mod poison; pub mod remutex; pub mod rwlock; pub mod thread; pub mod thread_info; pub mod thread_local; +pub mod unwind; +pub mod util; pub mod wtf8; #[cfg(any(all(unix, not(any(target_os = "macos", target_os = "ios"))), @@ -52,3 +74,27 @@ pub trait IntoInner { pub trait FromInner { fn from_inner(inner: Inner) -> Self; } + +/// Enqueues a procedure to run when the main thread exits. +/// +/// Currently these closures are only run once the main *Rust* thread exits. +/// Once the `at_exit` handlers begin running, more may be enqueued, but not +/// infinitely so. Eventually a handler registration will be forced to fail. +/// +/// Returns `Ok` if the handler was successfully registered, meaning that the +/// closure will be run once the main thread exits. Returns `Err` to indicate +/// that the closure could not be registered, meaning that it is not scheduled +/// to be run. +pub fn at_exit(f: F) -> Result<(), ()> { + if at_exit_imp::push(Box::new(f)) {Ok(())} else {Err(())} +} + +/// One-time runtime cleanup. +pub fn cleanup() { + static CLEANUP: Once = Once::new(); + CLEANUP.call_once(|| unsafe { + args::cleanup(); + sys::stack_overflow::cleanup(); + at_exit_imp::cleanup(); + }); +} diff --git a/src/libstd/sys/common/unwind/gcc.rs b/src/libstd/sys/common/unwind/gcc.rs new file mode 100644 index 00000000000..361cef08c11 --- /dev/null +++ b/src/libstd/sys/common/unwind/gcc.rs @@ -0,0 +1,233 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(private_no_mangle_fns)] + +use prelude::v1::*; + +use any::Any; +use sys_common::libunwind as uw; + +struct Exception { + uwe: uw::_Unwind_Exception, + cause: Option>, +} + +pub unsafe fn panic(data: Box) -> ! { + let exception: Box<_> = box Exception { + uwe: uw::_Unwind_Exception { + exception_class: rust_exception_class(), + exception_cleanup: exception_cleanup, + private: [0; uw::unwinder_private_data_size], + }, + cause: Some(data), + }; + let exception_param = Box::into_raw(exception) as *mut uw::_Unwind_Exception; + let error = uw::_Unwind_RaiseException(exception_param); + rtabort!("Could not unwind stack, error = {}", error as isize); + + extern fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code, + exception: *mut uw::_Unwind_Exception) { + unsafe { + let _: Box = Box::from_raw(exception as *mut Exception); + } + } +} + +pub unsafe fn cleanup(ptr: *mut u8) -> Box { + let my_ep = ptr as *mut Exception; + let cause = (*my_ep).cause.take(); + uw::_Unwind_DeleteException(ptr as *mut _); + cause.unwrap() +} + +// Rust's exception class identifier. This is used by personality routines to +// determine whether the exception was thrown by their own runtime. +fn rust_exception_class() -> uw::_Unwind_Exception_Class { + // M O Z \0 R U S T -- vendor, language + 0x4d4f5a_00_52555354 +} + +// We could implement our personality routine in pure Rust, however exception +// info decoding is tedious. More importantly, personality routines have to +// handle various platform quirks, which are not fun to maintain. For this +// reason, we attempt to reuse personality routine of the C language: +// __gcc_personality_v0. +// +// Since C does not support exception catching, __gcc_personality_v0 simply +// always returns _URC_CONTINUE_UNWIND in search phase, and always returns +// _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase. +// +// This is pretty close to Rust's exception handling approach, except that Rust +// does have a single "catch-all" handler at the bottom of each thread's stack. +// So we have two versions of the personality routine: +// - rust_eh_personality, used by all cleanup landing pads, which never catches, +// so the behavior of __gcc_personality_v0 is perfectly adequate there, and +// - rust_eh_personality_catch, used only by rust_try(), which always catches. +// +// See also: rustc_trans::trans::intrinsic::trans_gnu_try + +#[cfg(all(not(target_arch = "arm"), + not(all(windows, target_arch = "x86_64")), + not(test)))] +pub mod eabi { + use sys_common::libunwind as uw; + use libc::c_int; + + extern { + fn __gcc_personality_v0(version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code; + } + + #[lang = "eh_personality"] + #[no_mangle] + extern fn rust_eh_personality( + version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + unsafe { + __gcc_personality_v0(version, actions, exception_class, ue_header, + context) + } + } + + #[lang = "eh_personality_catch"] + #[no_mangle] + pub extern fn rust_eh_personality_catch( + version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + + if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase + uw::_URC_HANDLER_FOUND // catch! + } + else { // cleanup phase + unsafe { + __gcc_personality_v0(version, actions, exception_class, ue_header, + context) + } + } + } +} + +// iOS on armv7 is using SjLj exceptions and therefore requires to use +// a specialized personality routine: __gcc_personality_sj0 + +#[cfg(all(target_os = "ios", target_arch = "arm", not(test)))] +pub mod eabi { + use sys_common::libunwind as uw; + use libc::c_int; + + extern { + fn __gcc_personality_sj0(version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code; + } + + #[lang = "eh_personality"] + #[no_mangle] + pub extern fn rust_eh_personality( + version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + unsafe { + __gcc_personality_sj0(version, actions, exception_class, ue_header, + context) + } + } + + #[lang = "eh_personality_catch"] + #[no_mangle] + pub extern fn rust_eh_personality_catch( + version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase + uw::_URC_HANDLER_FOUND // catch! + } + else { // cleanup phase + unsafe { + __gcc_personality_sj0(version, actions, exception_class, ue_header, + context) + } + } + } +} + + +// ARM EHABI uses a slightly different personality routine signature, +// but otherwise works the same. +#[cfg(all(target_arch = "arm", not(target_os = "ios"), not(test)))] +pub mod eabi { + use sys_common::libunwind as uw; + use libc::c_int; + + extern { + fn __gcc_personality_v0(state: uw::_Unwind_State, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code; + } + + #[lang = "eh_personality"] + #[no_mangle] + extern fn rust_eh_personality( + state: uw::_Unwind_State, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + unsafe { + __gcc_personality_v0(state, ue_header, context) + } + } + + #[lang = "eh_personality_catch"] + #[no_mangle] + pub extern fn rust_eh_personality_catch( + state: uw::_Unwind_State, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + if (state as c_int & uw::_US_ACTION_MASK as c_int) + == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { // search phase + uw::_URC_HANDLER_FOUND // catch! + } + else { // cleanup phase + unsafe { + __gcc_personality_v0(state, ue_header, context) + } + } + } +} diff --git a/src/libstd/sys/common/unwind/mod.rs b/src/libstd/sys/common/unwind/mod.rs new file mode 100644 index 00000000000..ff93d0526b7 --- /dev/null +++ b/src/libstd/sys/common/unwind/mod.rs @@ -0,0 +1,325 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation of Rust stack unwinding +//! +//! For background on exception handling and stack unwinding please see +//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and +//! documents linked from it. +//! These are also good reads: +//! http://mentorembedded.github.io/cxx-abi/abi-eh.html +//! http://monoinfinito.wordpress.com/series/exception-handling-in-c/ +//! http://www.airs.com/blog/index.php?s=exception+frames +//! +//! ## A brief summary +//! +//! Exception handling happens in two phases: a search phase and a cleanup phase. +//! +//! In both phases the unwinder walks stack frames from top to bottom using +//! information from the stack frame unwind sections of the current process's +//! modules ("module" here refers to an OS module, i.e. an executable or a +//! dynamic library). +//! +//! For each stack frame, it invokes the associated "personality routine", whose +//! address is also stored in the unwind info section. +//! +//! In the search phase, the job of a personality routine is to examine exception +//! object being thrown, and to decide whether it should be caught at that stack +//! frame. Once the handler frame has been identified, cleanup phase begins. +//! +//! In the cleanup phase, personality routines invoke cleanup code associated +//! with their stack frames (i.e. destructors). Once stack has been unwound down +//! to the handler frame level, unwinding stops and the last personality routine +//! transfers control to its catch block. +//! +//! ## Frame unwind info registration +//! +//! Each module has its own frame unwind info section (usually ".eh_frame"), and +//! unwinder needs to know about all of them in order for unwinding to be able to +//! cross module boundaries. +//! +//! On some platforms, like Linux, this is achieved by dynamically enumerating +//! currently loaded modules via the dl_iterate_phdr() API and finding all +//! .eh_frame sections. +//! +//! Others, like Windows, require modules to actively register their unwind info +//! sections by calling __register_frame_info() API at startup. In the latter +//! case it is essential that there is only one copy of the unwinder runtime in +//! the process. This is usually achieved by linking to the dynamic version of +//! the unwind runtime. +//! +//! Currently Rust uses unwind runtime provided by libgcc. + +#![allow(dead_code)] +#![allow(unused_imports)] + +use prelude::v1::*; + +use any::Any; +use boxed; +use cell::Cell; +use cmp; +use panicking; +use fmt; +use intrinsics; +use mem; +use sync::atomic::{self, Ordering}; +use sys_common::mutex::Mutex; + +// The actual unwinding implementation is cfg'd here, and we've got two current +// implementations. One goes through SEH on Windows and the other goes through +// libgcc via the libunwind-like API. + +// i686-pc-windows-msvc +#[cfg(all(windows, target_arch = "x86", target_env = "msvc"))] +#[path = "seh.rs"] #[doc(hidden)] +pub mod imp; + +// x86_64-pc-windows-* +#[cfg(all(windows, target_arch = "x86_64"))] +#[path = "seh64_gnu.rs"] #[doc(hidden)] +pub mod imp; + +// i686-pc-windows-gnu and all others +#[cfg(any(unix, all(windows, target_arch = "x86", target_env = "gnu")))] +#[path = "gcc.rs"] #[doc(hidden)] +pub mod imp; + +pub type Callback = fn(msg: &(Any + Send), file: &'static str, line: u32); + +// Variables used for invoking callbacks when a thread starts to unwind. +// +// For more information, see below. +const MAX_CALLBACKS: usize = 16; +static CALLBACKS: [atomic::AtomicUsize; MAX_CALLBACKS] = + [atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), + atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), + atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), + atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), + atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), + atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), + atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), + atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0)]; +static CALLBACK_CNT: atomic::AtomicUsize = atomic::AtomicUsize::new(0); + +thread_local! { static PANICKING: Cell = Cell::new(false) } + +/// Invoke a closure, capturing the cause of panic if one occurs. +/// +/// This function will return `Ok(())` if the closure did not panic, and will +/// return `Err(cause)` if the closure panics. The `cause` returned is the +/// object with which panic was originally invoked. +/// +/// This function also is unsafe for a variety of reasons: +/// +/// * This is not safe to call in a nested fashion. The unwinding +/// interface for Rust is designed to have at most one try/catch block per +/// thread, not multiple. No runtime checking is currently performed to uphold +/// this invariant, so this function is not safe. A nested try/catch block +/// may result in corruption of the outer try/catch block's state, especially +/// if this is used within a thread itself. +/// +/// * It is not sound to trigger unwinding while already unwinding. Rust threads +/// have runtime checks in place to ensure this invariant, but it is not +/// guaranteed that a rust thread is in place when invoking this function. +/// Unwinding twice can lead to resource leaks where some destructors are not +/// run. +pub unsafe fn try(f: F) -> Result<(), Box> { + let mut f = Some(f); + return inner_try(try_fn::, &mut f as *mut _ as *mut u8); + + // If an inner function were not used here, then this generic function `try` + // uses the native symbol `rust_try`, for which the code is statically + // linked into the standard library. This means that the DLL for the + // standard library must have `rust_try` as an exposed symbol that + // downstream crates can link against (because monomorphizations of `try` in + // downstream crates will have a reference to the `rust_try` symbol). + // + // On MSVC this requires the symbol `rust_try` to be tagged with + // `dllexport`, but it's easier to not have conditional `src/rt/rust_try.ll` + // files and instead just have this non-generic shim the compiler can take + // care of exposing correctly. + unsafe fn inner_try(f: fn(*mut u8), data: *mut u8) + -> Result<(), Box> { + let prev = PANICKING.with(|s| s.get()); + PANICKING.with(|s| s.set(false)); + let ep = intrinsics::try(f, data); + PANICKING.with(|s| s.set(prev)); + if ep.is_null() { + Ok(()) + } else { + Err(imp::cleanup(ep)) + } + } + + fn try_fn(opt_closure: *mut u8) { + let opt_closure = opt_closure as *mut Option; + unsafe { (*opt_closure).take().unwrap()(); } + } + + extern { + // Rust's try-catch + // When f(...) returns normally, the return value is null. + // When f(...) throws, the return value is a pointer to the caught + // exception object. + fn rust_try(f: extern fn(*mut u8), + data: *mut u8) -> *mut u8; + } +} + +/// Determines whether the current thread is unwinding because of panic. +pub fn panicking() -> bool { + PANICKING.with(|s| s.get()) +} + +// An uninlined, unmangled function upon which to slap yer breakpoints +#[inline(never)] +#[no_mangle] +#[allow(private_no_mangle_fns)] +fn rust_panic(cause: Box) -> ! { + unsafe { + imp::panic(cause) + } +} + +#[cfg(not(test))] +/// Entry point of panic from the libcore crate. +#[lang = "panic_fmt"] +pub extern fn rust_begin_unwind(msg: fmt::Arguments, + file: &'static str, line: u32) -> ! { + begin_unwind_fmt(msg, &(file, line)) +} + +/// The entry point for unwinding with a formatted message. +/// +/// This is designed to reduce the amount of code required at the call +/// site as much as possible (so that `panic!()` has as low an impact +/// on (e.g.) the inlining of other functions as possible), by moving +/// the actual formatting into this shared place. +#[inline(never)] #[cold] +pub fn begin_unwind_fmt(msg: fmt::Arguments, file_line: &(&'static str, u32)) -> ! { + use fmt::Write; + + // We do two allocations here, unfortunately. But (a) they're + // required with the current scheme, and (b) we don't handle + // panic + OOM properly anyway (see comment in begin_unwind + // below). + + let mut s = String::new(); + let _ = s.write_fmt(msg); + begin_unwind_inner(Box::new(s), file_line) +} + +/// This is the entry point of unwinding for panic!() and assert!(). +#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible +pub fn begin_unwind(msg: M, file_line: &(&'static str, u32)) -> ! { + // Note that this should be the only allocation performed in this code path. + // Currently this means that panic!() on OOM will invoke this code path, + // but then again we're not really ready for panic on OOM anyway. If + // we do start doing this, then we should propagate this allocation to + // be performed in the parent of this thread instead of the thread that's + // panicking. + + // see below for why we do the `Any` coercion here. + begin_unwind_inner(Box::new(msg), file_line) +} + +/// The core of the unwinding. +/// +/// This is non-generic to avoid instantiation bloat in other crates +/// (which makes compilation of small crates noticeably slower). (Note: +/// we need the `Any` object anyway, we're not just creating it to +/// avoid being generic.) +/// +/// Doing this split took the LLVM IR line counts of `fn main() { panic!() +/// }` from ~1900/3700 (-O/no opts) to 180/590. +#[inline(never)] #[cold] // this is the slow path, please never inline this +fn begin_unwind_inner(msg: Box, + file_line: &(&'static str, u32)) -> ! { + // Make sure the default failure handler is registered before we look at the + // callbacks. We also use a raw sys-based mutex here instead of a + // `std::sync` one as accessing TLS can cause weird recursive problems (and + // we don't need poison checking). + unsafe { + static LOCK: Mutex = Mutex::new(); + static mut INIT: bool = false; + LOCK.lock(); + if !INIT { + register(panicking::on_panic); + INIT = true; + } + LOCK.unlock(); + } + + // First, invoke call the user-defined callbacks triggered on thread panic. + // + // By the time that we see a callback has been registered (by reading + // MAX_CALLBACKS), the actual callback itself may have not been stored yet, + // so we just chalk it up to a race condition and move on to the next + // callback. Additionally, CALLBACK_CNT may briefly be higher than + // MAX_CALLBACKS, so we're sure to clamp it as necessary. + let callbacks = { + let amt = CALLBACK_CNT.load(Ordering::SeqCst); + &CALLBACKS[..cmp::min(amt, MAX_CALLBACKS)] + }; + for cb in callbacks { + match cb.load(Ordering::SeqCst) { + 0 => {} + n => { + let f: Callback = unsafe { mem::transmute(n) }; + let (file, line) = *file_line; + f(&*msg, file, line); + } + } + }; + + // Now that we've run all the necessary unwind callbacks, we actually + // perform the unwinding. + if panicking() { + // If a thread panics while it's already unwinding then we + // have limited options. Currently our preference is to + // just abort. In the future we may consider resuming + // unwinding or otherwise exiting the thread cleanly. + super::util::dumb_print(format_args!("thread panicked while panicking. \ + aborting.")); + unsafe { intrinsics::abort() } + } + PANICKING.with(|s| s.set(true)); + rust_panic(msg); +} + +/// Register a callback to be invoked when a thread unwinds. +/// +/// This is an unsafe and experimental API which allows for an arbitrary +/// callback to be invoked when a thread panics. This callback is invoked on both +/// the initial unwinding and a double unwinding if one occurs. Additionally, +/// the local `Thread` will be in place for the duration of the callback, and +/// the callback must ensure that it remains in place once the callback returns. +/// +/// Only a limited number of callbacks can be registered, and this function +/// returns whether the callback was successfully registered or not. It is not +/// currently possible to unregister a callback once it has been registered. +pub unsafe fn register(f: Callback) -> bool { + match CALLBACK_CNT.fetch_add(1, Ordering::SeqCst) { + // The invocation code has knowledge of this window where the count has + // been incremented, but the callback has not been stored. We're + // guaranteed that the slot we're storing into is 0. + n if n < MAX_CALLBACKS => { + let prev = CALLBACKS[n].swap(mem::transmute(f), Ordering::SeqCst); + rtassert!(prev == 0); + true + } + // If we accidentally bumped the count too high, pull it back. + _ => { + CALLBACK_CNT.store(MAX_CALLBACKS, Ordering::SeqCst); + false + } + } +} diff --git a/src/libstd/sys/common/unwind/seh.rs b/src/libstd/sys/common/unwind/seh.rs new file mode 100644 index 00000000000..a201e406a23 --- /dev/null +++ b/src/libstd/sys/common/unwind/seh.rs @@ -0,0 +1,146 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx) +//! +//! On Windows (currently only on MSVC), the default exception handling +//! mechanism is Structured Exception Handling (SEH). This is quite different +//! than Dwarf-based exception handling (e.g. what other unix platforms use) in +//! terms of compiler internals, so LLVM is required to have a good deal of +//! extra support for SEH. Currently this support is somewhat lacking, so what's +//! here is the bare bones of SEH support. +//! +//! In a nutshell, what happens here is: +//! +//! 1. The `panic` function calls the standard Windows function `RaiseException` +//! with a Rust-specific code, triggering the unwinding process. +//! 2. All landing pads generated by the compiler (just "cleanup" landing pads) +//! use the personality function `__C_specific_handler`, a function in the +//! CRT, and the unwinding code in Windows will use this personality function +//! to execute all cleanup code on the stack. +//! 3. Eventually the "catch" code in `rust_try` (located in +//! src/rt/rust_try_msvc_64.ll) is executed, which will ensure that the +//! exception being caught is indeed a Rust exception, returning control back +//! into Rust. +//! +//! Some specific differences from the gcc-based exception handling are: +//! +//! * Rust has no custom personality function, it is instead *always* +//! __C_specific_handler, so the filtering is done in a C++-like manner +//! instead of in the personality function itself. Note that the specific +//! syntax for this (found in the rust_try_msvc_64.ll) is taken from an LLVM +//! test case for SEH. +//! * We've got some data to transmit across the unwinding boundary, +//! specifically a `Box`. In Dwarf-based unwinding this +//! data is part of the payload of the exception, but I have not currently +//! figured out how to do this with LLVM's bindings. Judging by some comments +//! in the LLVM test cases this may not even be possible currently with LLVM, +//! so this is just abandoned entirely. Instead the data is stored in a +//! thread-local in `panic` and retrieved during `cleanup`. +//! +//! So given all that, the bindings here are pretty small, + +#![allow(bad_style)] + +use prelude::v1::*; + +use any::Any; +use libc::{c_ulong, DWORD, c_void}; +use ptr; +use sys_common::thread_local::StaticKey; + +// 0x R U S T +const RUST_PANIC: DWORD = 0x52555354; +static PANIC_DATA: StaticKey = StaticKey::new(None); + +// This function is provided by kernel32.dll +extern "system" { + fn RaiseException(dwExceptionCode: DWORD, + dwExceptionFlags: DWORD, + nNumberOfArguments: DWORD, + lpArguments: *const c_ulong); +} + +#[repr(C)] +pub struct EXCEPTION_POINTERS { + ExceptionRecord: *mut EXCEPTION_RECORD, + ContextRecord: *mut CONTEXT, +} + +enum CONTEXT {} + +#[repr(C)] +struct EXCEPTION_RECORD { + ExceptionCode: DWORD, + ExceptionFlags: DWORD, + ExceptionRecord: *mut _EXCEPTION_RECORD, + ExceptionAddress: *mut c_void, + NumberParameters: DWORD, + ExceptionInformation: [*mut c_ulong; EXCEPTION_MAXIMUM_PARAMETERS], +} + +enum _EXCEPTION_RECORD {} + +const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15; + +pub unsafe fn panic(data: Box) -> ! { + // See module docs above for an explanation of why `data` is stored in a + // thread local instead of being passed as an argument to the + // `RaiseException` function (which can in theory carry along arbitrary + // data). + let exception = Box::new(data); + rtassert!(PANIC_DATA.get().is_null()); + PANIC_DATA.set(Box::into_raw(exception) as *mut u8); + + RaiseException(RUST_PANIC, 0, 0, ptr::null()); + rtabort!("could not unwind stack"); +} + +pub unsafe fn cleanup(ptr: *mut u8) -> Box { + // The `ptr` here actually corresponds to the code of the exception, and our + // real data is stored in our thread local. + rtassert!(ptr as DWORD == RUST_PANIC); + + let data = PANIC_DATA.get() as *mut Box; + PANIC_DATA.set(ptr::null_mut()); + rtassert!(!data.is_null()); + + *Box::from_raw(data) +} + +// This is required by the compiler to exist (e.g. it's a lang item), but it's +// never actually called by the compiler because __C_specific_handler is the +// personality function that is always used. Hence this is just an aborting +// stub. +#[lang = "eh_personality"] +fn rust_eh_personality() { + unsafe { ::intrinsics::abort() } +} + +// This is a function referenced from `rust_try_msvc_64.ll` which is used to +// filter the exceptions being caught by that function. +// +// In theory local variables can be accessed through the `rbp` parameter of this +// function, but a comment in an LLVM test case indicates that this is not +// implemented in LLVM, so this is just an idempotent function which doesn't +// ferry along any other information. +// +// This function just takes a look at the current EXCEPTION_RECORD being thrown +// to ensure that it's code is RUST_PANIC, which was set by the call to +// `RaiseException` above in the `panic` function. +#[lang = "msvc_try_filter"] +#[linkage = "external"] +#[allow(private_no_mangle_fns)] +extern fn __rust_try_filter(eh_ptrs: *mut EXCEPTION_POINTERS, + _rbp: *mut u8) -> i32 { + unsafe { + ((*(*eh_ptrs).ExceptionRecord).ExceptionCode == RUST_PANIC) as i32 + } +} diff --git a/src/libstd/sys/common/unwind/seh64_gnu.rs b/src/libstd/sys/common/unwind/seh64_gnu.rs new file mode 100644 index 00000000000..4d23794de24 --- /dev/null +++ b/src/libstd/sys/common/unwind/seh64_gnu.rs @@ -0,0 +1,218 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Unwinding implementation of top of native Win64 SEH, +//! however the unwind handler data (aka LSDA) uses GCC-compatible encoding. + +#![allow(bad_style)] +#![allow(private_no_mangle_fns)] + +use prelude::v1::*; + +use any::Any; +use self::EXCEPTION_DISPOSITION::*; +use sys_common::dwarf::eh; +use core::mem; +use core::ptr; +use libc::{c_void, c_ulonglong, DWORD, LPVOID}; +type ULONG_PTR = c_ulonglong; + +// Define our exception codes: +// according to http://msdn.microsoft.com/en-us/library/het71c37(v=VS.80).aspx, +// [31:30] = 3 (error), 2 (warning), 1 (info), 0 (success) +// [29] = 1 (user-defined) +// [28] = 0 (reserved) +// we define bits: +// [24:27] = type +// [0:23] = magic +const ETYPE: DWORD = 0b1110_u32 << 28; +const MAGIC: DWORD = 0x525354; // "RST" + +const RUST_PANIC: DWORD = ETYPE | (1 << 24) | MAGIC; + +const EXCEPTION_NONCONTINUABLE: DWORD = 0x1; // Noncontinuable exception +const EXCEPTION_UNWINDING: DWORD = 0x2; // Unwind is in progress +const EXCEPTION_EXIT_UNWIND: DWORD = 0x4; // Exit unwind is in progress +const EXCEPTION_STACK_INVALID: DWORD = 0x8; // Stack out of limits or unaligned +const EXCEPTION_NESTED_CALL: DWORD = 0x10; // Nested exception handler call +const EXCEPTION_TARGET_UNWIND: DWORD = 0x20; // Target unwind in progress +const EXCEPTION_COLLIDED_UNWIND: DWORD = 0x40; // Collided exception handler call +const EXCEPTION_UNWIND: DWORD = EXCEPTION_UNWINDING | + EXCEPTION_EXIT_UNWIND | + EXCEPTION_TARGET_UNWIND | + EXCEPTION_COLLIDED_UNWIND; + +#[repr(C)] +pub struct EXCEPTION_RECORD { + ExceptionCode: DWORD, + ExceptionFlags: DWORD, + ExceptionRecord: *const EXCEPTION_RECORD, + ExceptionAddress: LPVOID, + NumberParameters: DWORD, + ExceptionInformation: [ULONG_PTR; 15], +} + +pub enum CONTEXT {} +pub enum UNWIND_HISTORY_TABLE {} + +#[repr(C)] +pub struct RUNTIME_FUNCTION { + BeginAddress: DWORD, + EndAddress: DWORD, + UnwindData: DWORD, +} + +#[repr(C)] +pub struct DISPATCHER_CONTEXT { + ControlPc: LPVOID, + ImageBase: LPVOID, + FunctionEntry: *const RUNTIME_FUNCTION, + EstablisherFrame: LPVOID, + TargetIp: LPVOID, + ContextRecord: *const CONTEXT, + LanguageHandler: LPVOID, + HandlerData: *const u8, + HistoryTable: *const UNWIND_HISTORY_TABLE, +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub enum EXCEPTION_DISPOSITION { + ExceptionContinueExecution, + ExceptionContinueSearch, + ExceptionNestedException, + ExceptionCollidedUnwind +} + +// From kernel32.dll +extern "system" { + fn RaiseException(dwExceptionCode: DWORD, + dwExceptionFlags: DWORD, + nNumberOfArguments: DWORD, + lpArguments: *const ULONG_PTR); + + fn RtlUnwindEx(TargetFrame: LPVOID, + TargetIp: LPVOID, + ExceptionRecord: *const EXCEPTION_RECORD, + ReturnValue: LPVOID, + OriginalContext: *const CONTEXT, + HistoryTable: *const UNWIND_HISTORY_TABLE); +} + +#[repr(C)] +struct PanicData { + data: Box +} + +pub unsafe fn panic(data: Box) -> ! { + let panic_ctx = Box::new(PanicData { data: data }); + let params = [Box::into_raw(panic_ctx) as ULONG_PTR]; + RaiseException(RUST_PANIC, + EXCEPTION_NONCONTINUABLE, + params.len() as DWORD, + ¶ms as *const ULONG_PTR); + rtabort!("could not unwind stack"); +} + +pub unsafe fn cleanup(ptr: *mut u8) -> Box { + let panic_ctx = Box::from_raw(ptr as *mut PanicData); + return panic_ctx.data; +} + +// SEH doesn't support resuming unwinds after calling a landing pad like +// libunwind does. For this reason, MSVC compiler outlines landing pads into +// separate functions that can be called directly from the personality function +// but are nevertheless able to find and modify stack frame of the "parent" +// function. +// +// Since this cannot be done with libdwarf-style landing pads, +// rust_eh_personality instead catches RUST_PANICs, runs the landing pad, then +// reraises the exception. +// +// Note that it makes certain assumptions about the exception: +// +// 1. That RUST_PANIC is non-continuable, so no lower stack frame may choose to +// resume execution. +// 2. That the first parameter of the exception is a pointer to an extra data +// area (PanicData). +// Since these assumptions do not generally hold true for foreign exceptions +// (system faults, C++ exceptions, etc), we make no attempt to invoke our +// landing pads (and, thus, destructors!) for anything other than RUST_PANICs. +// This is considered acceptable, because the behavior of throwing exceptions +// through a C ABI boundary is undefined. + +#[lang = "eh_personality_catch"] +#[cfg(not(test))] +unsafe extern fn rust_eh_personality_catch( + exceptionRecord: *mut EXCEPTION_RECORD, + establisherFrame: LPVOID, + contextRecord: *mut CONTEXT, + dispatcherContext: *mut DISPATCHER_CONTEXT +) -> EXCEPTION_DISPOSITION +{ + rust_eh_personality(exceptionRecord, establisherFrame, + contextRecord, dispatcherContext) +} + +#[lang = "eh_personality"] +#[cfg(not(test))] +unsafe extern fn rust_eh_personality( + exceptionRecord: *mut EXCEPTION_RECORD, + establisherFrame: LPVOID, + contextRecord: *mut CONTEXT, + dispatcherContext: *mut DISPATCHER_CONTEXT +) -> EXCEPTION_DISPOSITION +{ + let er = &*exceptionRecord; + let dc = &*dispatcherContext; + + if er.ExceptionFlags & EXCEPTION_UNWIND == 0 { // we are in the dispatch phase + if er.ExceptionCode == RUST_PANIC { + if let Some(lpad) = find_landing_pad(dc) { + RtlUnwindEx(establisherFrame, + lpad as LPVOID, + exceptionRecord, + er.ExceptionInformation[0] as LPVOID, // pointer to PanicData + contextRecord, + dc.HistoryTable); + rtabort!("could not unwind"); + } + } + } + ExceptionContinueSearch +} + +// The `resume` instruction, found at the end of the landing pads, and whose job +// is to resume stack unwinding, is typically lowered by LLVM into a call to +// `_Unwind_Resume` routine. To avoid confusion with the same symbol exported +// from libgcc, we redirect it to `rust_eh_unwind_resume`. +// Since resolution of this symbol is done by the linker, `rust_eh_unwind_resume` +// must be marked `pub` + `#[no_mangle]`. (Can we make it a lang item?) + +#[lang = "eh_unwind_resume"] +#[cfg(not(test))] +unsafe extern fn rust_eh_unwind_resume(panic_ctx: LPVOID) { + let params = [panic_ctx as ULONG_PTR]; + RaiseException(RUST_PANIC, + EXCEPTION_NONCONTINUABLE, + params.len() as DWORD, + ¶ms as *const ULONG_PTR); + rtabort!("could not resume unwind"); +} + +unsafe fn find_landing_pad(dc: &DISPATCHER_CONTEXT) -> Option { + let eh_ctx = eh::EHContext { + ip: dc.ControlPc as usize, + func_start: dc.ImageBase as usize + (*dc.FunctionEntry).BeginAddress as usize, + text_start: dc.ImageBase as usize, + data_start: 0 + }; + eh::find_landing_pad(dc.HandlerData, &eh_ctx) +} diff --git a/src/libstd/sys/common/util.rs b/src/libstd/sys/common/util.rs new file mode 100644 index 00000000000..979f1f48669 --- /dev/null +++ b/src/libstd/sys/common/util.rs @@ -0,0 +1,46 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use env; +use fmt; +use intrinsics; +use io::prelude::*; +use sync::atomic::{self, Ordering}; +use sys::stdio::Stderr; +use thread; + +pub fn min_stack() -> usize { + static MIN: atomic::AtomicUsize = atomic::AtomicUsize::new(0); + match MIN.load(Ordering::SeqCst) { + 0 => {} + n => return n - 1, + } + let amt = env::var("RUST_MIN_STACK").ok().and_then(|s| s.parse().ok()); + let amt = amt.unwrap_or(2 * 1024 * 1024); + // 0 is our sentinel value, so ensure that we'll never see 0 after + // initialization has run + MIN.store(amt + 1, Ordering::SeqCst); + amt +} + +pub fn dumb_print(args: fmt::Arguments) { + let _ = Stderr::new().map(|mut stderr| stderr.write_fmt(args)); +} + +pub fn abort(args: fmt::Arguments) -> ! { + dumb_print(format_args!("fatal runtime error: {}", args)); + unsafe { intrinsics::abort(); } +} + +#[allow(dead_code)] // stack overflow detection not enabled on all platforms +pub unsafe fn report_overflow() { + dumb_print(format_args!("\nthread '{}' has overflowed its stack", + thread::current().name().unwrap_or(""))); +} diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index bbed42cc31d..964bc08ff4e 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -12,6 +12,7 @@ #![allow(non_camel_case_types)] use io::{self, ErrorKind}; +use libc::funcs::posix01::signal::signal; use libc; use num::One; use ops::Neg; @@ -47,6 +48,19 @@ pub mod thread_local; pub mod time; pub mod stdio; +pub fn init() { + // By default, some platforms will send a *signal* when a EPIPE error + // would otherwise be delivered. This runtime doesn't install a SIGPIPE + // handler, causing it to kill the program, which isn't exactly what we + // want! + // + // Hence, we set SIGPIPE to ignore when the program starts up in order + // to prevent this problem. + unsafe { + assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != !0); + } +} + pub fn decode_error_kind(errno: i32) -> ErrorKind { match errno as libc::c_int { libc::ECONNREFUSED => ErrorKind::ConnectionRefused, diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index af0d8da05f4..94c4d04ea30 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -358,8 +358,8 @@ pub fn args() -> Args { target_os = "netbsd", target_os = "openbsd"))] pub fn args() -> Args { - use rt; - let bytes = rt::args::clone().unwrap_or(Vec::new()); + use sys_common; + let bytes = sys_common::args::clone().unwrap_or(Vec::new()); let v: Vec = bytes.into_iter().map(|v| { OsStringExt::from_vec(v) }).collect(); diff --git a/src/libstd/sys/unix/stack_overflow.rs b/src/libstd/sys/unix/stack_overflow.rs index de9e8cf97e6..441313bc639 100644 --- a/src/libstd/sys/unix/stack_overflow.rs +++ b/src/libstd/sys/unix/stack_overflow.rs @@ -38,7 +38,7 @@ impl Drop for Handler { target_os = "openbsd"))] mod imp { use super::Handler; - use rt::util::report_overflow; + use sys_common::util::report_overflow; use mem; use ptr; use sys::c::{siginfo, sigaction, SIGBUS, SIG_DFL, diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 732e2e65864..04bb5e5ea38 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -43,6 +43,8 @@ pub mod thread_local; pub mod time; pub mod stdio; +pub fn init() {} + pub fn decode_error_kind(errno: i32) -> ErrorKind { match errno as libc::c_int { libc::ERROR_ACCESS_DENIED => ErrorKind::PermissionDenied, diff --git a/src/libstd/sys/windows/net.rs b/src/libstd/sys/windows/net.rs index e62b2d8cb18..998b4fcb1a1 100644 --- a/src/libstd/sys/windows/net.rs +++ b/src/libstd/sys/windows/net.rs @@ -16,11 +16,10 @@ use net::SocketAddr; use num::One; use ops::Neg; use ptr; -use rt; use sync::Once; use sys; use sys::c; -use sys_common::{AsInner, FromInner, IntoInner}; +use sys_common::{self, AsInner, FromInner, IntoInner}; use sys_common::net::{setsockopt, getsockopt}; use time::Duration; @@ -39,7 +38,7 @@ pub fn init() { &mut data); assert_eq!(ret, 0); - let _ = rt::at_exit(|| { c::WSACleanup(); }); + let _ = sys_common::at_exit(|| { c::WSACleanup(); }); }); } diff --git a/src/libstd/sys/windows/stack_overflow.rs b/src/libstd/sys/windows/stack_overflow.rs index f93f7c756de..d1c2144ef0d 100644 --- a/src/libstd/sys/windows/stack_overflow.rs +++ b/src/libstd/sys/windows/stack_overflow.rs @@ -9,7 +9,7 @@ // except according to those terms. use libc::{self, LONG}; -use rt::util::report_overflow; +use sys_common::util::report_overflow; use sys::c; pub struct Handler; diff --git a/src/libstd/sys/windows/thread_local.rs b/src/libstd/sys/windows/thread_local.rs index 17bc7ee8876..c544eec7fce 100644 --- a/src/libstd/sys/windows/thread_local.rs +++ b/src/libstd/sys/windows/thread_local.rs @@ -13,7 +13,7 @@ use prelude::v1::*; use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL}; use ptr; -use rt; +use sys_common; use sys_common::mutex::Mutex; pub type Key = DWORD; @@ -133,7 +133,7 @@ unsafe fn init_dtors() { let dtors = box Vec::<(Key, Dtor)>::new(); - let res = rt::at_exit(move|| { + let res = sys_common::at_exit(move|| { DTOR_LOCK.lock(); let dtors = DTORS; DTORS = 1 as *mut _; diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 3a4c3e7eef1..2b7a3deceb7 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -167,10 +167,11 @@ use any::Any; use cell::UnsafeCell; use fmt; use io; -use rt::{self, unwind}; use sync::{Mutex, Condvar, Arc}; use sys::thread as imp; use sys_common::thread_info; +use sys_common::unwind; +use sys_common::util; use time::Duration; //////////////////////////////////////////////////////////////////////////////// @@ -260,7 +261,7 @@ impl Builder { -> io::Result> { let Builder { name, stack_size } = self; - let stack_size = stack_size.unwrap_or(rt::min_stack()); + let stack_size = stack_size.unwrap_or(util::min_stack()); let my_thread = Thread::new(name); let their_thread = my_thread.clone(); @@ -383,7 +384,7 @@ pub fn catch_panic(f: F) -> Result let mut result = None; unsafe { let result = &mut result; - try!(::rt::unwind::try(move || *result = Some(f()))) + try!(unwind::try(move || *result = Some(f()))) } Ok(result.unwrap()) } -- cgit 1.4.1-3-g733a5