diff options
Diffstat (limited to 'src/libstd/sys')
| -rw-r--r-- | src/libstd/sys/common/args.rs | 99 | ||||
| -rw-r--r-- | src/libstd/sys/common/at_exit_imp.rs | 82 | ||||
| -rw-r--r-- | src/libstd/sys/common/backtrace.rs | 65 | ||||
| -rw-r--r-- | src/libstd/sys/common/dwarf/eh.rs | 159 | ||||
| -rw-r--r-- | src/libstd/sys/common/dwarf/mod.rs | 107 | ||||
| -rw-r--r-- | src/libstd/sys/common/libunwind.rs | 145 | ||||
| -rw-r--r-- | src/libstd/sys/common/mod.rs | 48 | ||||
| -rw-r--r-- | src/libstd/sys/common/unwind/gcc.rs | 233 | ||||
| -rw-r--r-- | src/libstd/sys/common/unwind/mod.rs | 325 | ||||
| -rw-r--r-- | src/libstd/sys/common/unwind/seh.rs | 146 | ||||
| -rw-r--r-- | src/libstd/sys/common/unwind/seh64_gnu.rs | 218 | ||||
| -rw-r--r-- | src/libstd/sys/common/util.rs | 46 | ||||
| -rw-r--r-- | src/libstd/sys/unix/mod.rs | 14 | ||||
| -rw-r--r-- | src/libstd/sys/unix/os.rs | 4 | ||||
| -rw-r--r-- | src/libstd/sys/unix/stack_overflow.rs | 2 | ||||
| -rw-r--r-- | src/libstd/sys/windows/mod.rs | 2 | ||||
| -rw-r--r-- | src/libstd/sys/windows/net.rs | 5 | ||||
| -rw-r--r-- | src/libstd/sys/windows/stack_overflow.rs | 2 | ||||
| -rw-r--r-- | src/libstd/sys/windows/thread_local.rs | 4 |
19 files changed, 1694 insertions, 12 deletions
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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Vec<Vec<u8>>> { 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<Vec<Vec<u8>>> { + let _guard = LOCK.lock(); + unsafe { + let ptr = get_global_ptr(); + (*ptr).as_ref().map(|s| (**s).clone()) + } + } + + fn get_global_ptr() -> *mut Option<Box<Vec<Vec<u8>>>> { + 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<Vec<Vec<u8>>> { + 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Box<FnBox()>>; + +// 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<Queue> = 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<Queue> = Box::from_raw(queue); + for to_run in *queue { + to_run(); + } + } + } + } +} + +pub fn push(f: Box<FnBox()>) -> 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 +//! (<root>/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<usize> { + if lsda.is_null() { + return None; + } + + let func_start = context.func_start; + let mut reader = DwarfReader::new(lsda); + + let start_encoding = reader.read::<u8>(); + // 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::<u8>(); + 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::<u8>(); + 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::<usize>()) as *const u8; + return reader.read::<usize>(); + } + + let mut result = match encoding & 0x0F { + DW_EH_PE_absptr => reader.read::<usize>(), + DW_EH_PE_uleb128 => reader.read_uleb128() as usize, + DW_EH_PE_udata2 => reader.read::<u16>() as usize, + DW_EH_PE_udata4 => reader.read::<u32>() as usize, + DW_EH_PE_udata8 => reader.read::<u64>() as usize, + DW_EH_PE_sleb128 => reader.read_sleb128() as usize, + DW_EH_PE_sdata2 => reader.read::<i16>() as usize, + DW_EH_PE_sdata4 => reader.read::<i32>() as usize, + DW_EH_PE_sdata8 => reader.read::<i64>() 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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>(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<T:Copy>(&mut self) -> T { + let Unaligned(result) = *(self.ptr as *const Unaligned<T>); + self.ptr = self.ptr.offset(mem::size_of::<T>() 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::<u8>(); + 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::<u8>(); + result |= ((byte & 0x7F) as u64) << shift; + shift += 7; + if byte & 0x80 == 0 { + break; + } + } + // sign-extend + if shift < 8 * mem::size_of::<u64>() && (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>() == u8::to_be(1u8)); + assert!(reader.read::<u16>() == u16::to_be(0x0203)); + assert!(reader.read::<u32>() == u32::to_be(0x04050607)); + + assert!(reader.read_uleb128() == 624485); + assert!(reader.read_sleb128() == -624485); + + assert!(reader.read::<i8>() == 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Inner> { pub trait FromInner<Inner> { 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: FnOnce() + Send + 'static>(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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Box<Any + Send + 'static>>, +} + +pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! { + 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<Exception> = Box::from_raw(exception as *mut Exception); + } + } +} + +pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> { + 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<bool> = 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: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> { + let mut f = Some(f); + return inner_try(try_fn::<F>, &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<Any + Send>> { + 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<F: FnOnce()>(opt_closure: *mut u8) { + let opt_closure = opt_closure as *mut Option<F>; + 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<Any + Send + 'static>) -> ! { + 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<M: Any + Send>(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<Any + Send>, + 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Any + Send + 'static>`. 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<Any + Send + 'static>) -> ! { + // 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<Any + Send + 'static> { + // 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<Any + Send + 'static>; + 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Any + Send + 'static> +} + +pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! { + 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<Any + Send + 'static> { + 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<usize> { + 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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("<unknown>"))); +} 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<OsString> = 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 _; |
