diff --git a/mk/crates.mk b/mk/crates.mk index 48c1e4ad59d5b..1583515014a39 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -53,7 +53,8 @@ TARGET_CRATES := libc std term \ getopts collections test rand \ core alloc \ rustc_unicode rustc_bitflags \ - alloc_system alloc_jemalloc + alloc_system alloc_jemalloc \ + panic_abort panic_unwind unwind RUSTC_CRATES := rustc rustc_typeck rustc_mir rustc_borrowck rustc_resolve rustc_driver \ rustc_trans rustc_back rustc_llvm rustc_privacy rustc_lint \ rustc_data_structures rustc_platform_intrinsics \ @@ -72,10 +73,18 @@ DEPS_libc := core DEPS_rand := core DEPS_rustc_bitflags := core DEPS_rustc_unicode := core +DEPS_panic_abort := libc alloc +DEPS_panic_unwind := libc alloc unwind +DEPS_unwind := libc + +# FIXME(stage0): change this to just `RUSTFLAGS_panic_abort := ...` +RUSTFLAGS1_panic_abort := -C panic=abort +RUSTFLAGS2_panic_abort := -C panic=abort +RUSTFLAGS3_panic_abort := -C panic=abort DEPS_std := core libc rand alloc collections rustc_unicode \ native:backtrace \ - alloc_system + alloc_system panic_abort panic_unwind unwind DEPS_arena := std DEPS_glob := std DEPS_flate := std native:miniz @@ -148,6 +157,9 @@ ONLY_RLIB_rustc_unicode := 1 ONLY_RLIB_rustc_bitflags := 1 ONLY_RLIB_alloc_system := 1 ONLY_RLIB_alloc_jemalloc := 1 +ONLY_RLIB_panic_unwind := 1 +ONLY_RLIB_panic_abort := 1 +ONLY_RLIB_unwind := 1 TARGET_SPECIFIC_alloc_jemalloc := 1 diff --git a/mk/tests.mk b/mk/tests.mk index cc712413d3b1c..ea610f63dbf22 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -23,7 +23,8 @@ DEPS_collectionstest := $(eval $(call RUST_CRATE,collectionstest)) TEST_TARGET_CRATES = $(filter-out core rustc_unicode alloc_system libc \ - alloc_jemalloc,$(TARGET_CRATES)) \ + alloc_jemalloc panic_unwind \ + panic_abort,$(TARGET_CRATES)) \ collectionstest coretest TEST_DOC_CRATES = $(DOC_CRATES) arena flate fmt_macros getopts graphviz \ log rand rbml serialize syntax term test diff --git a/src/bootstrap/rustc.rs b/src/bootstrap/rustc.rs index 99e16035d1d4f..046bc34438c42 100644 --- a/src/bootstrap/rustc.rs +++ b/src/bootstrap/rustc.rs @@ -48,10 +48,11 @@ fn main() { } else { env::var_os("RUSTC_REAL").unwrap() }; + let stage = env::var("RUSTC_STAGE").unwrap(); let mut cmd = Command::new(rustc); cmd.args(&args) - .arg("--cfg").arg(format!("stage{}", env::var("RUSTC_STAGE").unwrap())); + .arg("--cfg").arg(format!("stage{}", stage)); if let Some(target) = target { // The stage0 compiler has a special sysroot distinct from what we @@ -78,6 +79,22 @@ fn main() { cmd.args(&s.split(" ").filter(|s| !s.is_empty()).collect::>()); } + // If we're compiling specifically the `panic_abort` crate then we pass + // the `-C panic=abort` option. Note that we do not do this for any + // other crate intentionally as this is the only crate for now that we + // ship with panic=abort. + // + // This... is a bit of a hack how we detect this. Ideally this + // information should be encoded in the crate I guess? Would likely + // require an RFC amendment to RFC 1513, however. + let is_panic_abort = args.windows(2).any(|a| { + &*a[0] == "--crate-name" && &*a[1] == "panic_abort" + }); + // FIXME(stage0): remove this `stage != "0"` condition + if is_panic_abort && stage != "0" { + cmd.arg("-C").arg("panic=abort"); + } + // Set various options from config.toml to configure how we're building // code. if env::var("RUSTC_DEBUGINFO") == Ok("true".to_string()) { diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index ca4c9bfd9544c..a22299c5e1a54 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -18,10 +18,8 @@ form or name", issue = "27783")] #![feature(allocator)] -#![feature(libc)] #![feature(staged_api)] - -extern crate libc; +#![cfg_attr(unix, feature(libc))] // The minimum alignment guaranteed by the architecture. This value is used to // add fast paths for low alignment values. In practice, the alignment is a @@ -72,9 +70,10 @@ pub extern "C" fn __rust_usable_size(size: usize, align: usize) -> usize { #[cfg(unix)] mod imp { + extern crate libc; + use core::cmp; use core::ptr; - use libc; use MIN_ALIGN; pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 { diff --git a/src/libpanic_abort/Cargo.toml b/src/libpanic_abort/Cargo.toml new file mode 100644 index 0000000000000..a7905703f596f --- /dev/null +++ b/src/libpanic_abort/Cargo.toml @@ -0,0 +1,11 @@ +[package] +authors = ["The Rust Project Developers"] +name = "panic_abort" +version = "0.0.0" + +[lib] +path = "lib.rs" + +[dependencies] +core = { path = "../libcore" } +libc = { path = "../rustc/libc_shim" } diff --git a/src/libpanic_abort/lib.rs b/src/libpanic_abort/lib.rs new file mode 100644 index 0000000000000..9802f66a5543d --- /dev/null +++ b/src/libpanic_abort/lib.rs @@ -0,0 +1,112 @@ +// Copyright 2016 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 panics via process aborts +//! +//! When compared to the implementation via unwinding, this crate is *much* +//! simpler! That being said, it's not quite as versatile, but here goes! + +#![no_std] +#![crate_name = "panic_abort"] +#![crate_type = "rlib"] +#![unstable(feature = "panic_abort", issue = "32837")] +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/", + issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")] +#![cfg_attr(not(stage0), deny(warnings))] + +#![feature(staged_api)] + +#![cfg_attr(not(stage0), panic_runtime)] +#![cfg_attr(not(stage0), feature(panic_runtime))] +#![cfg_attr(unix, feature(libc))] +#![cfg_attr(windows, feature(core_intrinsics))] + +// Rust's "try" function, but if we're aborting on panics we just call the +// function as there's nothing else we need to do here. +#[no_mangle] +pub unsafe extern fn __rust_maybe_catch_panic(f: fn(*mut u8), + data: *mut u8, + _data_ptr: *mut usize, + _vtable_ptr: *mut usize) -> u32 { + f(data); + 0 +} + +// "Leak" the payload and shim to the relevant abort on the platform in +// question. +// +// For Unix we just use `abort` from libc as it'll trigger debuggers, core +// dumps, etc, as one might expect. On Windows, however, the best option we have +// is the `__fastfail` intrinsics, but that's unfortunately not defined in LLVM, +// and the `RaiseFailFastException` function isn't available until Windows 7 +// which would break compat with XP. For now just use `intrinsics::abort` which +// will kill us with an illegal instruction, which will do a good enough job for +// now hopefully. +#[no_mangle] +pub unsafe extern fn __rust_start_panic(_data: usize, _vtable: usize) -> u32 { + return abort(); + + #[cfg(unix)] + unsafe fn abort() -> ! { + extern crate libc; + libc::abort(); + } + + #[cfg(windows)] + unsafe fn abort() -> ! { + core::intrinsics::abort(); + } +} + +// This... is a bit of an oddity. The tl;dr; is that this is required to link +// correctly, the longer explanation is below. +// +// Right now the binaries of libcore/libstd that we ship are all compiled with +// `-C panic=unwind`. This is done to ensure that the binaries are maximally +// compatible with as many situations as possible. The compiler, however, +// requires a "personality function" for all functions compiled with `-C +// panic=unwind`. This personality function is hardcoded to the symbol +// `rust_eh_personality` and is defined by the `eh_personality` lang item. +// +// So... why not just define that lang item here? Good question! The way that +// panic runtimes are linked in is actually a little subtle in that they're +// "sort of" in the compiler's crate store, but only actually linked if another +// isn't actually linked. This ends up meaning that both this crate and the +// panic_unwind crate can appear in the compiler's crate store, and if both +// define the `eh_personality` lang item then that'll hit an error. +// +// To handle this the compiler only requires the `eh_personality` is defined if +// the panic runtime being linked in is the unwinding runtime, and otherwise +// it's not required to be defined (rightfully so). In this case, however, this +// library just defines this symbol so there's at least some personality +// somewhere. +// +// Essentially this symbol is just defined to get wired up to libcore/libstd +// binaries, but it should never be called as we don't link in an unwinding +// runtime at all. +#[no_mangle] +#[cfg(not(stage0))] +pub extern fn rust_eh_personality() {} + +// Similar to above, this corresponds to the `eh_unwind_resume` lang item that's +// only used on Windows currently. +#[no_mangle] +#[cfg(all(not(stage0), target_os = "windows", target_env = "gnu"))] +pub extern fn rust_eh_unwind_resume() {} + +#[no_mangle] +#[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86"))] +pub extern fn rust_eh_register_frames() {} + +#[no_mangle] +#[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86"))] +pub extern fn rust_eh_unregister_frames() {} diff --git a/src/libpanic_unwind/Cargo.lock b/src/libpanic_unwind/Cargo.lock new file mode 100644 index 0000000000000..20d826d4a470e --- /dev/null +++ b/src/libpanic_unwind/Cargo.lock @@ -0,0 +1,27 @@ +[root] +name = "panic_unwind" +version = "0.0.0" +dependencies = [ + "alloc 0.0.0", + "core 0.0.0", + "libc 0.0.0", +] + +[[package]] +name = "alloc" +version = "0.0.0" +dependencies = [ + "core 0.0.0", +] + +[[package]] +name = "core" +version = "0.0.0" + +[[package]] +name = "libc" +version = "0.0.0" +dependencies = [ + "core 0.0.0", +] + diff --git a/src/libpanic_unwind/Cargo.toml b/src/libpanic_unwind/Cargo.toml new file mode 100644 index 0000000000000..27edecd6f9668 --- /dev/null +++ b/src/libpanic_unwind/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Rust Project Developers"] +name = "panic_unwind" +version = "0.0.0" + +[lib] +path = "lib.rs" + +[dependencies] +alloc = { path = "../liballoc" } +core = { path = "../libcore" } +libc = { path = "../rustc/libc_shim" } +unwind = { path = "../libunwind" } diff --git a/src/libstd/sys/common/dwarf/eh.rs b/src/libpanic_unwind/dwarf/eh.rs similarity index 99% rename from src/libstd/sys/common/dwarf/eh.rs rename to src/libpanic_unwind/dwarf/eh.rs index 319be245bde98..1c3fca98a1f71 100644 --- a/src/libstd/sys/common/dwarf/eh.rs +++ b/src/libpanic_unwind/dwarf/eh.rs @@ -21,8 +21,7 @@ #![allow(non_upper_case_globals)] #![allow(unused)] -use prelude::v1::*; -use sys_common::dwarf::DwarfReader; +use dwarf::DwarfReader; use core::mem; pub const DW_EH_PE_omit : u8 = 0xFF; diff --git a/src/libstd/sys/common/dwarf/mod.rs b/src/libpanic_unwind/dwarf/mod.rs similarity index 99% rename from src/libstd/sys/common/dwarf/mod.rs rename to src/libpanic_unwind/dwarf/mod.rs index 822826bcc837f..cde21f90811de 100644 --- a/src/libstd/sys/common/dwarf/mod.rs +++ b/src/libpanic_unwind/dwarf/mod.rs @@ -18,7 +18,6 @@ pub mod eh; -use prelude::v1::*; use core::mem; pub struct DwarfReader { diff --git a/src/libstd/sys/common/unwind/gcc.rs b/src/libpanic_unwind/gcc.rs similarity index 68% rename from src/libstd/sys/common/unwind/gcc.rs rename to src/libpanic_unwind/gcc.rs index da7a340af3515..50b2e1534d70d 100644 --- a/src/libstd/sys/common/unwind/gcc.rs +++ b/src/libpanic_unwind/gcc.rs @@ -8,30 +8,76 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! Implementation of panics backed by libgcc/libunwind (in some form) +//! +//! 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, the unwinder invokes each personality routine again. +//! This time it decides which (if any) cleanup code needs to be run for +//! the current stack frame. If so, the control is transferred to a special +//! branch in the function body, the "landing pad", which invokes destructors, +//! frees memory, etc. At the end of the landing pad, control is transferred +//! back to the unwinder and unwinding resumes. +//! +//! Once stack has been unwound down to the handler frame level, unwinding stops +//! and the last personality routine transfers control to the catch block. +//! +//! ## `eh_personality` and `eh_unwind_resume` +//! +//! These language items are used by the compiler when generating unwind info. +//! The first one is the personality routine described above. The second one +//! allows compilation target to customize the process of resuming unwind at the +//! end of the landing pads. `eh_unwind_resume` is used only if +//! `custom_unwind_resume` flag in the target options is set. + #![allow(private_no_mangle_fns)] -use prelude::v1::*; +use core::any::Any; +use alloc::boxed::Box; -use any::Any; -use sys_common::libunwind as uw; +use unwind as uw; +#[repr(C)] struct Exception { - uwe: uw::_Unwind_Exception, - cause: Option>, + _uwe: uw::_Unwind_Exception, + cause: Option>, } -pub unsafe fn panic(data: Box) -> ! { - let exception: Box<_> = box Exception { - uwe: uw::_Unwind_Exception { +pub unsafe fn panic(data: Box) -> u32 { + let exception = Box::new(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); + return uw::_Unwind_RaiseException(exception_param) as u32; extern fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code, exception: *mut uw::_Unwind_Exception) { @@ -45,7 +91,7 @@ pub fn payload() -> *mut u8 { 0 as *mut u8 } -pub unsafe fn cleanup(ptr: *mut u8) -> Box { +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 _); @@ -59,7 +105,7 @@ fn rust_exception_class() -> uw::_Unwind_Exception_Class { 0x4d4f5a_00_52555354 } -// We could implement our personality routine in pure Rust, however exception +// We could implement our personality routine in 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: @@ -79,10 +125,9 @@ fn rust_exception_class() -> uw::_Unwind_Exception_Class { // See also: rustc_trans::trans::intrinsic::trans_gnu_try #[cfg(all(not(target_arch = "arm"), - not(all(windows, target_arch = "x86_64")), - not(test)))] + not(all(windows, target_arch = "x86_64"))))] pub mod eabi { - use sys_common::libunwind as uw; + use unwind as uw; use libc::c_int; extern { @@ -136,9 +181,9 @@ pub mod eabi { // 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)))] +#[cfg(all(target_os = "ios", target_arch = "arm"))] pub mod eabi { - use sys_common::libunwind as uw; + use unwind as uw; use libc::c_int; extern { @@ -191,9 +236,9 @@ pub mod eabi { // 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)))] +#[cfg(all(target_arch = "arm", not(target_os = "ios")))] pub mod eabi { - use sys_common::libunwind as uw; + use unwind as uw; use libc::c_int; extern { @@ -242,19 +287,31 @@ pub mod eabi { } // See docs in the `unwind` module. -#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu", not(test)))] +#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] #[lang = "eh_unwind_resume"] #[unwind] unsafe extern fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! { uw::_Unwind_Resume(panic_ctx as *mut uw::_Unwind_Exception); } +// Frame unwind info registration +// +// Each module's image contains a frame unwind info section (usually +// ".eh_frame"). When a module is loaded/unloaded into the process, the +// unwinder must be informed about the location of this section in memory. The +// methods of achieving that vary by the platform. On some (e.g. Linux), the +// unwinder can discover unwind info sections on its own (by dynamically +// enumerating currently loaded modules via the dl_iterate_phdr() API and +// finding their ".eh_frame" sections); Others, like Windows, require modules +// to actively register their unwind info sections via unwinder API. +// +// This module defines two symbols which are referenced and called from +// rsbegin.rs to reigster our information with the GCC runtime. The +// implementation of stack unwinding is (for now) deferred to libgcc_eh, however +// Rust crates use these Rust-specific entry points to avoid potential clashes +// with any GCC runtime. #[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] pub mod eh_frame_registry { - // The implementation of stack unwinding is (for now) deferred to libgcc_eh, however Rust - // crates use these Rust-specific entry points to avoid potential clashes with GCC runtime. - // See also: rtbegin.rs, `unwind` module. - #[link(name = "gcc_eh")] #[cfg(not(cargobuild))] extern {} @@ -263,16 +320,14 @@ pub mod eh_frame_registry { fn __register_frame_info(eh_frame_begin: *const u8, object: *mut u8); fn __deregister_frame_info(eh_frame_begin: *const u8, object: *mut u8); } - #[cfg(not(test))] + #[no_mangle] - #[unstable(feature = "libstd_sys_internals", issue = "0")] pub unsafe extern fn rust_eh_register_frames(eh_frame_begin: *const u8, object: *mut u8) { __register_frame_info(eh_frame_begin, object); } - #[cfg(not(test))] + #[no_mangle] - #[unstable(feature = "libstd_sys_internals", issue = "0")] pub unsafe extern fn rust_eh_unregister_frames(eh_frame_begin: *const u8, object: *mut u8) { __deregister_frame_info(eh_frame_begin, object); diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs new file mode 100644 index 0000000000000..17cbd2e0d4c39 --- /dev/null +++ b/src/libpanic_unwind/lib.rs @@ -0,0 +1,109 @@ +// Copyright 2016 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 panics via stack unwinding +//! +//! This crate is an implementation of panics in Rust using "most native" stack +//! unwinding mechanism of the platform this is being compiled for. This +//! essentially gets categorized into three buckets currently: +//! +//! 1. MSVC targets use SEH in the `seh.rs` file. +//! 2. The 64-bit MinGW target half-uses SEH and half-use gcc-like information +//! in the `seh64_gnu.rs` module. +//! 3. All other targets use libunwind/libgcc in the `gcc/mod.rs` module. +//! +//! More documentation about each implementation can be found in the respective +//! module. + +#![no_std] +#![crate_name = "panic_unwind"] +#![crate_type = "rlib"] +#![unstable(feature = "panic_unwind", issue = "32837")] +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/", + issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")] +#![cfg_attr(not(stage0), deny(warnings))] + +#![feature(alloc)] +#![feature(core_intrinsics)] +#![feature(lang_items)] +#![feature(libc)] +#![feature(panic_unwind)] +#![feature(raw)] +#![feature(staged_api)] +#![feature(unwind_attributes)] +#![cfg_attr(target_env = "msvc", feature(raw))] + +#![cfg_attr(not(stage0), panic_runtime)] +#![cfg_attr(not(stage0), feature(panic_runtime))] + +extern crate alloc; +extern crate libc; +extern crate unwind; + +use core::intrinsics; +use core::mem; +use core::raw; + +// Rust runtime's startup objects depend on these symbols, so make them public. +#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] +pub use imp::eh_frame_registry::*; + +// *-pc-windows-msvc +#[cfg(target_env = "msvc")] +#[path = "seh.rs"] +mod imp; + +// x86_64-pc-windows-gnu +#[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))] +#[path = "seh64_gnu.rs"] +mod imp; + +// i686-pc-windows-gnu and all others +#[cfg(any(unix, all(windows, target_arch = "x86", target_env = "gnu")))] +#[path = "gcc.rs"] +mod imp; + +mod dwarf; +mod windows; + +// Entry point for catching an exception, implemented using the `try` intrinsic +// in the compiler. +// +// The interaction between the `payload` function and the compiler is pretty +// hairy and tightly coupled, for more information see the compiler's +// implementation of this. +#[no_mangle] +pub unsafe extern fn __rust_maybe_catch_panic(f: fn(*mut u8), + data: *mut u8, + data_ptr: *mut usize, + vtable_ptr: *mut usize) + -> u32 { + let mut payload = imp::payload(); + if intrinsics::try(f, data, &mut payload as *mut _ as *mut _) == 0 { + 0 + } else { + let obj = mem::transmute::<_, raw::TraitObject>(imp::cleanup(payload)); + *data_ptr = obj.data as usize; + *vtable_ptr = obj.vtable as usize; + 1 + } +} + +// Entry point for raising an exception, just delegates to the platform-specific +// implementation. +#[no_mangle] +pub unsafe extern fn __rust_start_panic(data: usize, vtable: usize) -> u32 { + imp::panic(mem::transmute(raw::TraitObject { + data: data as *mut (), + vtable: vtable as *mut (), + })) +} diff --git a/src/libstd/sys/common/unwind/seh.rs b/src/libpanic_unwind/seh.rs similarity index 52% rename from src/libstd/sys/common/unwind/seh.rs rename to src/libpanic_unwind/seh.rs index 94da42f0092f5..c451eeca2371e 100644 --- a/src/libstd/sys/common/unwind/seh.rs +++ b/src/libpanic_unwind/seh.rs @@ -45,7 +45,7 @@ //! the precise codegen for this was lifted from an LLVM test case for SEH //! (this is the `__rust_try_filter` function below). //! * We've got some data to transmit across the unwinding boundary, -//! specifically a `Box`. Like with Dwarf exceptions +//! specifically a `Box`. Like with Dwarf exceptions //! these two pointers are stored as a payload in the exception itself. On //! MSVC, however, there's no need for an extra allocation because the call //! stack is preserved while filter functions are being executed. This means @@ -56,90 +56,84 @@ //! [win64]: http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx //! [llvm]: http://llvm.org/docs/ExceptionHandling.html#background-on-windows-exceptions -use sys::c; +use alloc::boxed::Box; +use core::any::Any; +use core::intrinsics; +use core::mem; +use core::raw; + +use windows as c; // A code which indicates panics that originate from Rust. Note that some of the // upper bits are used by the system so we just set them to 0 and ignore them. // 0x 0 R S T const RUST_PANIC: c::DWORD = 0x00525354; -pub use self::imp::*; - -mod imp { - use prelude::v1::*; - - use any::Any; - use mem; - use raw; - use super::RUST_PANIC; - use sys::c; - - pub unsafe fn panic(data: Box) -> ! { - // As mentioned above, the call stack here is preserved while the filter - // functions are running, so it's ok to pass stack-local arrays into - // `RaiseException`. - // - // The two pointers of the `data` trait object are written to the stack, - // passed to `RaiseException`, and they're later extracted by the filter - // function below in the "custom exception information" section of the - // `EXCEPTION_RECORD` type. - let ptrs = mem::transmute::<_, raw::TraitObject>(data); - let ptrs = [ptrs.data, ptrs.vtable]; - c::RaiseException(RUST_PANIC, 0, 2, ptrs.as_ptr() as *mut _); - rtabort!("could not unwind stack"); - } +pub unsafe fn panic(data: Box) -> u32 { + // As mentioned above, the call stack here is preserved while the filter + // functions are running, so it's ok to pass stack-local arrays into + // `RaiseException`. + // + // The two pointers of the `data` trait object are written to the stack, + // passed to `RaiseException`, and they're later extracted by the filter + // function below in the "custom exception information" section of the + // `EXCEPTION_RECORD` type. + let ptrs = mem::transmute::<_, raw::TraitObject>(data); + let ptrs = [ptrs.data, ptrs.vtable]; + c::RaiseException(RUST_PANIC, 0, 2, ptrs.as_ptr() as *mut _); + u32::max_value() +} - pub fn payload() -> [usize; 2] { - [0; 2] - } +pub fn payload() -> [usize; 2] { + [0; 2] +} - pub unsafe fn cleanup(payload: [usize; 2]) -> Box { - mem::transmute(raw::TraitObject { - data: payload[0] as *mut _, - vtable: payload[1] as *mut _, - }) - } +pub unsafe fn cleanup(payload: [usize; 2]) -> Box { + mem::transmute(raw::TraitObject { + data: payload[0] as *mut _, + vtable: payload[1] as *mut _, + }) +} - // This is quite a special function, and it's not literally passed in as the - // filter function for the `catchpad` of the `try` intrinsic. The compiler - // actually generates its own filter function wrapper which will delegate to - // this for the actual execution logic for whether the exception should be - // caught. The reasons for this are: - // - // * Each architecture has a slightly different ABI for the filter function - // here. For example on x86 there are no arguments but on x86_64 there are - // two. - // * This function needs access to the stack frame of the `try` intrinsic - // which is using this filter as a catch pad. This is because the payload - // of this exception, `Box`, needs to be transmitted to that - // location. - // - // Both of these differences end up using a ton of weird llvm-specific - // intrinsics, so it's actually pretty difficult to express the entire - // filter function in Rust itself. As a compromise, the compiler takes care - // of all the weird LLVM-specific and platform-specific stuff, getting to - // the point where this function makes the actual decision about what to - // catch given two parameters. - // - // The first parameter is `*mut EXCEPTION_POINTERS` which is some contextual - // information about the exception being filtered, and the second pointer is - // `*mut *mut [usize; 2]` (the payload here). This value points directly - // into the stack frame of the `try` intrinsic itself, and we use it to copy - // information from the exception onto the stack. - #[lang = "msvc_try_filter"] - #[cfg(not(test))] - unsafe extern fn __rust_try_filter(eh_ptrs: *mut u8, - payload: *mut u8) -> i32 { - let eh_ptrs = eh_ptrs as *mut c::EXCEPTION_POINTERS; - let payload = payload as *mut *mut [usize; 2]; - let record = &*(*eh_ptrs).ExceptionRecord; - if record.ExceptionCode != RUST_PANIC { - return 0 - } - (**payload)[0] = record.ExceptionInformation[0] as usize; - (**payload)[1] = record.ExceptionInformation[1] as usize; - return 1 +// This is quite a special function, and it's not literally passed in as the +// filter function for the `catchpad` of the `try` intrinsic. The compiler +// actually generates its own filter function wrapper which will delegate to +// this for the actual execution logic for whether the exception should be +// caught. The reasons for this are: +// +// * Each architecture has a slightly different ABI for the filter function +// here. For example on x86 there are no arguments but on x86_64 there are +// two. +// * This function needs access to the stack frame of the `try` intrinsic +// which is using this filter as a catch pad. This is because the payload +// of this exception, `Box`, needs to be transmitted to that +// location. +// +// Both of these differences end up using a ton of weird llvm-specific +// intrinsics, so it's actually pretty difficult to express the entire +// filter function in Rust itself. As a compromise, the compiler takes care +// of all the weird LLVM-specific and platform-specific stuff, getting to +// the point where this function makes the actual decision about what to +// catch given two parameters. +// +// The first parameter is `*mut EXCEPTION_POINTERS` which is some contextual +// information about the exception being filtered, and the second pointer is +// `*mut *mut [usize; 2]` (the payload here). This value points directly +// into the stack frame of the `try` intrinsic itself, and we use it to copy +// information from the exception onto the stack. +#[lang = "msvc_try_filter"] +#[cfg(not(test))] +unsafe extern fn __rust_try_filter(eh_ptrs: *mut u8, + payload: *mut u8) -> i32 { + let eh_ptrs = eh_ptrs as *mut c::EXCEPTION_POINTERS; + let payload = payload as *mut *mut [usize; 2]; + let record = &*(*eh_ptrs).ExceptionRecord; + if record.ExceptionCode != RUST_PANIC { + return 0 } + (**payload)[0] = record.ExceptionInformation[0] as usize; + (**payload)[1] = record.ExceptionInformation[1] as usize; + return 1 } // This is required by the compiler to exist (e.g. it's a lang item), but @@ -149,5 +143,5 @@ mod imp { #[lang = "eh_personality"] #[cfg(not(test))] fn rust_eh_personality() { - unsafe { ::intrinsics::abort() } + unsafe { intrinsics::abort() } } diff --git a/src/libstd/sys/common/unwind/seh64_gnu.rs b/src/libpanic_unwind/seh64_gnu.rs similarity index 92% rename from src/libstd/sys/common/unwind/seh64_gnu.rs rename to src/libpanic_unwind/seh64_gnu.rs index 57281d67ebb40..adb38d857eac7 100644 --- a/src/libstd/sys/common/unwind/seh64_gnu.rs +++ b/src/libpanic_unwind/seh64_gnu.rs @@ -14,13 +14,12 @@ #![allow(bad_style)] #![allow(private_no_mangle_fns)] -use prelude::v1::*; +use alloc::boxed::Box; -use any::Any; -use sys_common::dwarf::eh; -use core::mem; -use core::ptr; -use sys::c; +use core::any::Any; +use core::intrinsics; +use dwarf::eh; +use windows as c; // Define our exception codes: // according to http://msdn.microsoft.com/en-us/library/het71c37(v=VS.80).aspx, @@ -37,24 +36,24 @@ const RUST_PANIC: c::DWORD = ETYPE | (1 << 24) | MAGIC; #[repr(C)] struct PanicData { - data: Box + data: Box } -pub unsafe fn panic(data: Box) -> ! { +pub unsafe fn panic(data: Box) -> u32 { let panic_ctx = Box::new(PanicData { data: data }); let params = [Box::into_raw(panic_ctx) as c::ULONG_PTR]; c::RaiseException(RUST_PANIC, c::EXCEPTION_NONCONTINUABLE, params.len() as c::DWORD, ¶ms as *const c::ULONG_PTR); - rtabort!("could not unwind stack"); + u32::max_value() } pub fn payload() -> *mut u8 { 0 as *mut u8 } -pub unsafe fn cleanup(ptr: *mut u8) -> Box { +pub unsafe fn cleanup(ptr: *mut u8) -> Box { let panic_ctx = Box::from_raw(ptr as *mut PanicData); return panic_ctx.data; } @@ -115,14 +114,12 @@ unsafe extern fn rust_eh_personality( er.ExceptionInformation[0] as c::LPVOID, // pointer to PanicData contextRecord, dc.HistoryTable); - rtabort!("could not unwind"); } } } c::ExceptionContinueSearch } -#[cfg(not(test))] #[lang = "eh_unwind_resume"] #[unwind] unsafe extern fn rust_eh_unwind_resume(panic_ctx: c::LPVOID) -> ! { @@ -131,7 +128,7 @@ unsafe extern fn rust_eh_unwind_resume(panic_ctx: c::LPVOID) -> ! { c::EXCEPTION_NONCONTINUABLE, params.len() as c::DWORD, ¶ms as *const c::ULONG_PTR); - rtabort!("could not resume unwind"); + intrinsics::abort(); } unsafe fn find_landing_pad(dc: &c::DISPATCHER_CONTEXT) -> Option { diff --git a/src/libpanic_unwind/windows.rs b/src/libpanic_unwind/windows.rs new file mode 100644 index 0000000000000..a0ccbc0002880 --- /dev/null +++ b/src/libpanic_unwind/windows.rs @@ -0,0 +1,96 @@ +// 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. + +#![allow(bad_style)] +#![allow(dead_code)] +#![cfg(windows)] + +use libc::{c_void, c_ulong, c_long, c_ulonglong}; + +pub use self::EXCEPTION_DISPOSITION::*; +pub type DWORD = c_ulong; +pub type LONG = c_long; +pub type ULONG_PTR = c_ulonglong; +pub type LPVOID = *mut c_void; + +pub const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15; +pub const EXCEPTION_NONCONTINUABLE: DWORD = 0x1; // Noncontinuable exception +pub const EXCEPTION_UNWINDING: DWORD = 0x2; // Unwind is in progress +pub const EXCEPTION_EXIT_UNWIND: DWORD = 0x4; // Exit unwind is in progress +pub const EXCEPTION_TARGET_UNWIND: DWORD = 0x20; // Target unwind in progress +pub const EXCEPTION_COLLIDED_UNWIND: DWORD = 0x40; // Collided exception handler call +pub const EXCEPTION_UNWIND: DWORD = EXCEPTION_UNWINDING | + EXCEPTION_EXIT_UNWIND | + EXCEPTION_TARGET_UNWIND | + EXCEPTION_COLLIDED_UNWIND; + +#[repr(C)] +pub struct EXCEPTION_RECORD { + pub ExceptionCode: DWORD, + pub ExceptionFlags: DWORD, + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ExceptionAddress: LPVOID, + pub NumberParameters: DWORD, + pub ExceptionInformation: [LPVOID; EXCEPTION_MAXIMUM_PARAMETERS] +} + +#[repr(C)] +pub struct EXCEPTION_POINTERS { + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ContextRecord: *mut CONTEXT, +} + +pub enum UNWIND_HISTORY_TABLE {} + +#[repr(C)] +pub struct RUNTIME_FUNCTION { + pub BeginAddress: DWORD, + pub EndAddress: DWORD, + pub UnwindData: DWORD, +} + +pub enum CONTEXT {} + +#[repr(C)] +pub struct DISPATCHER_CONTEXT { + pub ControlPc: LPVOID, + pub ImageBase: LPVOID, + pub FunctionEntry: *const RUNTIME_FUNCTION, + pub EstablisherFrame: LPVOID, + pub TargetIp: LPVOID, + pub ContextRecord: *const CONTEXT, + pub LanguageHandler: LPVOID, + pub HandlerData: *const u8, + pub HistoryTable: *const UNWIND_HISTORY_TABLE, +} + +#[repr(C)] +#[allow(dead_code)] // we only use some variants +pub enum EXCEPTION_DISPOSITION { + ExceptionContinueExecution, + ExceptionContinueSearch, + ExceptionNestedException, + ExceptionCollidedUnwind +} + +extern "system" { + #[unwind] + pub fn RaiseException(dwExceptionCode: DWORD, + dwExceptionFlags: DWORD, + nNumberOfArguments: DWORD, + lpArguments: *const ULONG_PTR); + #[unwind] + pub fn RtlUnwindEx(TargetFrame: LPVOID, + TargetIp: LPVOID, + ExceptionRecord: *const EXCEPTION_RECORD, + ReturnValue: LPVOID, + OriginalContext: *const CONTEXT, + HistoryTable: *const UNWIND_HISTORY_TABLE); +} diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index c1a8f747de14d..035505655967c 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -31,6 +31,7 @@ use hir::def_id::{DefId, DefIndex}; use mir::repr::Mir; use mir::mir_map::MirMap; use session::Session; +use session::config::PanicStrategy; use session::search_paths::PathKind; use util::nodemap::{FnvHashMap, NodeMap, NodeSet, DefIdMap}; use std::any::Any; @@ -222,6 +223,8 @@ pub trait CrateStore<'tcx> : Any { fn is_staged_api(&self, cnum: ast::CrateNum) -> bool; fn is_explicitly_linked(&self, cnum: ast::CrateNum) -> bool; fn is_allocator(&self, cnum: ast::CrateNum) -> bool; + fn is_panic_runtime(&self, cnum: ast::CrateNum) -> bool; + fn panic_strategy(&self, cnum: ast::CrateNum) -> PanicStrategy; fn extern_crate(&self, cnum: ast::CrateNum) -> Option; fn crate_attrs(&self, cnum: ast::CrateNum) -> Vec; /// The name of the crate as it is referred to in source code of the current @@ -408,6 +411,10 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { fn is_staged_api(&self, cnum: ast::CrateNum) -> bool { bug!("is_staged_api") } fn is_explicitly_linked(&self, cnum: ast::CrateNum) -> bool { bug!("is_explicitly_linked") } fn is_allocator(&self, cnum: ast::CrateNum) -> bool { bug!("is_allocator") } + fn is_panic_runtime(&self, cnum: ast::CrateNum) -> bool { bug!("is_panic_runtime") } + fn panic_strategy(&self, cnum: ast::CrateNum) -> PanicStrategy { + bug!("panic_strategy") + } fn extern_crate(&self, cnum: ast::CrateNum) -> Option { bug!("extern_crate") } fn crate_attrs(&self, cnum: ast::CrateNum) -> Vec { bug!("crate_attrs") } diff --git a/src/librustc/middle/dependency_format.rs b/src/librustc/middle/dependency_format.rs index c2498b7b5eed3..fe22cfdb43f73 100644 --- a/src/librustc/middle/dependency_format.rs +++ b/src/librustc/middle/dependency_format.rs @@ -64,7 +64,7 @@ use syntax::ast; use session; -use session::config; +use session::config::{self, PanicStrategy}; use middle::cstore::LinkagePreference::{self, RequireStatic, RequireDynamic}; use util::nodemap::FnvHashMap; @@ -193,10 +193,15 @@ fn calculate_type(sess: &session::Session, } // We've gotten this far because we're emitting some form of a final - // artifact which means that we're going to need an allocator of some form. - // No allocator may have been required or linked so far, so activate one - // here if one isn't set. - activate_allocator(sess, &mut ret); + // artifact which means that we may need to inject dependencies of some + // form. + // + // Things like allocators and panic runtimes may not have been activated + // quite yet, so do so here. + activate_injected_dep(sess.injected_allocator.get(), &mut ret, + &|cnum| sess.cstore.is_allocator(cnum)); + activate_injected_dep(sess.injected_panic_runtime.get(), &mut ret, + &|cnum| sess.cstore.is_panic_runtime(cnum)); // When dylib B links to dylib A, then when using B we must also link to A. // It could be the case, however, that the rlib for A is present (hence we @@ -270,40 +275,42 @@ fn attempt_static(sess: &session::Session) -> Option { } }).collect::>(); - // Our allocator may not have been activated as it's not flagged with - // explicitly_linked, so flag it here if necessary. - activate_allocator(sess, &mut ret); + // Our allocator/panic runtime may not have been linked above if it wasn't + // explicitly linked, which is the case for any injected dependency. Handle + // that here and activate them. + activate_injected_dep(sess.injected_allocator.get(), &mut ret, + &|cnum| sess.cstore.is_allocator(cnum)); + activate_injected_dep(sess.injected_panic_runtime.get(), &mut ret, + &|cnum| sess.cstore.is_panic_runtime(cnum)); Some(ret) } // Given a list of how to link upstream dependencies so far, ensure that an -// allocator is activated. This will not do anything if one was transitively -// included already (e.g. via a dylib or explicitly so). +// injected dependency is activated. This will not do anything if one was +// transitively included already (e.g. via a dylib or explicitly so). // -// If an allocator was not found then we're guaranteed the metadata::creader -// module has injected an allocator dependency (not listed as a required -// dependency) in the session's `injected_allocator` field. If this field is not -// set then this compilation doesn't actually need an allocator and we can also -// skip this step entirely. -fn activate_allocator(sess: &session::Session, list: &mut DependencyList) { - let mut allocator_found = false; +// If an injected dependency was not found then we're guaranteed the +// metadata::creader module has injected that dependency (not listed as +// a required dependency) in one of the session's field. If this field is not +// set then this compilation doesn't actually need the dependency and we can +// also skip this step entirely. +fn activate_injected_dep(injected: Option, + list: &mut DependencyList, + replaces_injected: &Fn(ast::CrateNum) -> bool) { for (i, slot) in list.iter().enumerate() { let cnum = (i + 1) as ast::CrateNum; - if !sess.cstore.is_allocator(cnum) { + if !replaces_injected(cnum) { continue } - if let Linkage::NotLinked = *slot { - continue + if *slot != Linkage::NotLinked { + return } - allocator_found = true; } - if !allocator_found { - if let Some(injected_allocator) = sess.injected_allocator.get() { - let idx = injected_allocator as usize - 1; - assert_eq!(list[idx], Linkage::NotLinked); - list[idx] = Linkage::Static; - } + if let Some(injected) = injected { + let idx = injected as usize - 1; + assert_eq!(list[idx], Linkage::NotLinked); + list[idx] = Linkage::Static; } } @@ -314,21 +321,75 @@ fn verify_ok(sess: &session::Session, list: &[Linkage]) { return } let mut allocator = None; + let mut panic_runtime = None; for (i, linkage) in list.iter().enumerate() { - let cnum = (i + 1) as ast::CrateNum; - if !sess.cstore.is_allocator(cnum) { - continue - } if let Linkage::NotLinked = *linkage { continue } - if let Some(prev_alloc) = allocator { - let prev_name = sess.cstore.crate_name(prev_alloc); - let cur_name = sess.cstore.crate_name(cnum); - sess.err(&format!("cannot link together two \ - allocators: {} and {}", - prev_name, cur_name)); + let cnum = (i + 1) as ast::CrateNum; + if sess.cstore.is_allocator(cnum) { + if let Some(prev) = allocator { + let prev_name = sess.cstore.crate_name(prev); + let cur_name = sess.cstore.crate_name(cnum); + sess.err(&format!("cannot link together two \ + allocators: {} and {}", + prev_name, cur_name)); + } + allocator = Some(cnum); + } + + if sess.cstore.is_panic_runtime(cnum) { + if let Some((prev, _)) = panic_runtime { + let prev_name = sess.cstore.crate_name(prev); + let cur_name = sess.cstore.crate_name(cnum); + sess.err(&format!("cannot link together two \ + panic runtimes: {} and {}", + prev_name, cur_name)); + } + panic_runtime = Some((cnum, sess.cstore.panic_strategy(cnum))); + } + } + + // If we found a panic runtime, then we know by this point that it's the + // only one, but we perform validation here that all the panic strategy + // compilation modes for the whole DAG are valid. + if let Some((cnum, found_strategy)) = panic_runtime { + let desired_strategy = sess.opts.cg.panic.clone(); + + // First up, validate that our selected panic runtime is indeed exactly + // our same strategy. + if found_strategy != desired_strategy { + sess.err(&format!("the linked panic runtime `{}` is \ + not compiled with this crate's \ + panic strategy `{}`", + sess.cstore.crate_name(cnum), + desired_strategy.desc())); + } + + // Next up, verify that all other crates are compatible with this panic + // strategy. If the dep isn't linked, we ignore it, and if our strategy + // is abort then it's compatible with everything. Otherwise all crates' + // panic strategy must match our own. + for (i, linkage) in list.iter().enumerate() { + if let Linkage::NotLinked = *linkage { + continue + } + if desired_strategy == PanicStrategy::Abort { + continue + } + let cnum = (i + 1) as ast::CrateNum; + let found_strategy = sess.cstore.panic_strategy(cnum); + if desired_strategy == found_strategy { + continue + } + + sess.err(&format!("the crate `{}` is compiled with the \ + panic strategy `{}` which is \ + incompatible with this crate's \ + strategy of `{}`", + sess.cstore.crate_name(cnum), + found_strategy.desc(), + desired_strategy.desc())); } - allocator = Some(cnum); } } diff --git a/src/librustc/middle/weak_lang_items.rs b/src/librustc/middle/weak_lang_items.rs index 5fe6076e5380e..b7dfc86720458 100644 --- a/src/librustc/middle/weak_lang_items.rs +++ b/src/librustc/middle/weak_lang_items.rs @@ -10,7 +10,7 @@ //! Validity checking for weak lang items -use session::config; +use session::config::{self, PanicStrategy}; use session::Session; use middle::lang_items; @@ -75,7 +75,9 @@ fn verify(sess: &Session, items: &lang_items::LanguageItems) { config::CrateTypeRlib => false, } }); - if !needs_check { return } + if !needs_check { + return + } let mut missing = HashSet::new(); for cnum in sess.cstore.crates() { @@ -84,8 +86,19 @@ fn verify(sess: &Session, items: &lang_items::LanguageItems) { } } + // If we're not compiling with unwinding, we won't actually need these + // symbols. Other panic runtimes ensure that the relevant symbols are + // available to link things together, but they're never exercised. + let mut whitelisted = HashSet::new(); + if sess.opts.cg.panic != PanicStrategy::Unwind { + whitelisted.insert(lang_items::EhPersonalityLangItem); + whitelisted.insert(lang_items::EhUnwindResumeLangItem); + } + $( - if missing.contains(&lang_items::$item) && items.$name().is_none() { + if missing.contains(&lang_items::$item) && + !whitelisted.contains(&lang_items::$item) && + items.$name().is_none() { sess.err(&format!("language item required, but not found: `{}`", stringify!($name))); diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 1a2c1b9a09528..82e5ce07b13d3 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -317,6 +317,21 @@ impl Passes { } } +#[derive(Clone, PartialEq)] +pub enum PanicStrategy { + Unwind, + Abort, +} + +impl PanicStrategy { + pub fn desc(&self) -> &str { + match *self { + PanicStrategy::Unwind => "unwind", + PanicStrategy::Abort => "abort", + } + } +} + /// Declare a macro that will define all CodegenOptions/DebuggingOptions fields and parsers all /// at once. The goal of this macro is to define an interface that can be /// programmatically used by the option parser in order to initialize the struct @@ -402,11 +417,13 @@ macro_rules! options { Some("a space-separated list of passes, or `all`"); pub const parse_opt_uint: Option<&'static str> = Some("a number"); + pub const parse_panic_strategy: Option<&'static str> = + Some("either `panic` or `abort`"); } #[allow(dead_code)] mod $mod_set { - use super::{$struct_name, Passes, SomePasses, AllPasses}; + use super::{$struct_name, Passes, SomePasses, AllPasses, PanicStrategy}; $( pub fn $opt(cg: &mut $struct_name, v: Option<&str>) -> bool { @@ -510,6 +527,15 @@ macro_rules! options { } } } + + fn parse_panic_strategy(slot: &mut PanicStrategy, v: Option<&str>) -> bool { + match v { + Some("unwind") => *slot = PanicStrategy::Unwind, + Some("abort") => *slot = PanicStrategy::Abort, + _ => return false + } + true + } } ) } @@ -575,6 +601,8 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, "explicitly enable the cfg(debug_assertions) directive"), inline_threshold: Option = (None, parse_opt_uint, "set the inlining threshold for"), + panic: PanicStrategy = (PanicStrategy::Unwind, parse_panic_strategy, + "panic strategy to compile crate with"), } diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index edb1c4530c240..1bea01c4849e7 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -12,6 +12,7 @@ use lint; use middle::cstore::CrateStore; use middle::dependency_format; use session::search_paths::PathKind; +use session::config::PanicStrategy; use ty::tls; use util::nodemap::{NodeMap, FnvHashMap}; use mir::transform as mir_pass; @@ -82,9 +83,11 @@ pub struct Session { /// operations such as auto-dereference and monomorphization. pub recursion_limit: Cell, - /// The metadata::creader module may inject an allocator dependency if it - /// didn't already find one, and this tracks what was injected. + /// The metadata::creader module may inject an allocator/panic_runtime + /// dependency if it didn't already find one, and this tracks what was + /// injected. pub injected_allocator: Cell>, + pub injected_panic_runtime: Cell>, /// Names of all bang-style macros and syntax extensions /// available in this crate @@ -295,7 +298,8 @@ impl Session { self.opts.cg.lto } pub fn no_landing_pads(&self) -> bool { - self.opts.debugging_opts.no_landing_pads + self.opts.debugging_opts.no_landing_pads || + self.opts.cg.panic == PanicStrategy::Abort } pub fn unstable_options(&self) -> bool { self.opts.debugging_opts.unstable_options @@ -502,6 +506,7 @@ pub fn build_session_(sopts: config::Options, recursion_limit: Cell::new(64), next_node_id: Cell::new(1), injected_allocator: Cell::new(None), + injected_panic_runtime: Cell::new(None), available_macros: RefCell::new(HashSet::new()), imported_macro_spans: RefCell::new(HashMap::new()), }; diff --git a/src/librustc_metadata/common.rs b/src/librustc_metadata/common.rs index ea4e25754202c..2b972af07ff91 100644 --- a/src/librustc_metadata/common.rs +++ b/src/librustc_metadata/common.rs @@ -250,3 +250,5 @@ pub fn rustc_version() -> String { option_env!("CFG_VERSION").unwrap_or("unknown version") ) } + +pub const tag_panic_strategy: usize = 0x114; diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index de0de219db2f1..190e8552d199a 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -20,6 +20,7 @@ use loader::{self, CratePaths}; use rustc::hir::svh::Svh; use rustc::dep_graph::{DepGraph, DepNode}; use rustc::session::{config, Session}; +use rustc::session::config::PanicStrategy; use rustc::session::search_paths::PathKind; use rustc::middle::cstore::{CrateStore, validate_crate_name, ExternCrate}; use rustc::util::nodemap::FnvHashMap; @@ -630,6 +631,85 @@ impl<'a> CrateReader<'a> { } } + fn inject_panic_runtime(&mut self, krate: &ast::Crate) { + // If we're only compiling an rlib, then there's no need to select a + // panic runtime, so we just skip this section entirely. + let any_non_rlib = self.sess.crate_types.borrow().iter().any(|ct| { + *ct != config::CrateTypeRlib + }); + if !any_non_rlib { + info!("panic runtime injection skipped, only generating rlib"); + return + } + + // If we need a panic runtime, we try to find an existing one here. At + // the same time we perform some general validation of the DAG we've got + // going such as ensuring everything has a compatible panic strategy. + // + // The logic for finding the panic runtime here is pretty much the same + // as the allocator case with the only addition that the panic strategy + // compilation mode also comes into play. + let desired_strategy = self.sess.opts.cg.panic.clone(); + let mut runtime_found = false; + let mut needs_panic_runtime = attr::contains_name(&krate.attrs, + "needs_panic_runtime"); + self.cstore.iter_crate_data(|cnum, data| { + needs_panic_runtime = needs_panic_runtime || data.needs_panic_runtime(); + if data.is_panic_runtime() { + // Inject a dependency from all #![needs_panic_runtime] to this + // #![panic_runtime] crate. + self.inject_dependency_if(cnum, "a panic runtime", + &|data| data.needs_panic_runtime()); + runtime_found = runtime_found || data.explicitly_linked.get(); + } + }); + + // If an explicitly linked and matching panic runtime was found, or if + // we just don't need one at all, then we're done here and there's + // nothing else to do. + if !needs_panic_runtime || runtime_found { + return + } + + // By this point we know that we (a) need a panic runtime and (b) no + // panic runtime was explicitly linked. Here we just load an appropriate + // default runtime for our panic strategy and then inject the + // dependencies. + // + // We may resolve to an already loaded crate (as the crate may not have + // been explicitly linked prior to this) and we may re-inject + // dependencies again, but both of those situations are fine. + // + // Also note that we have yet to perform validation of the crate graph + // in terms of everyone has a compatible panic runtime format, that's + // performed later as part of the `dependency_format` module. + let name = match desired_strategy { + PanicStrategy::Unwind => "panic_unwind", + PanicStrategy::Abort => "panic_abort", + }; + info!("panic runtime not found -- loading {}", name); + + let (cnum, data, _) = self.resolve_crate(&None, name, name, None, + codemap::DUMMY_SP, + PathKind::Crate, false); + + // Sanity check the loaded crate to ensure it is indeed a panic runtime + // and the panic strategy is indeed what we thought it was. + if !data.is_panic_runtime() { + self.sess.err(&format!("the crate `{}` is not a panic runtime", + name)); + } + if data.panic_strategy() != desired_strategy { + self.sess.err(&format!("the crate `{}` does not have the panic \ + strategy `{}`", + name, desired_strategy.desc())); + } + + self.sess.injected_panic_runtime.set(Some(cnum)); + self.inject_dependency_if(cnum, "a panic runtime", + &|data| data.needs_panic_runtime()); + } + fn inject_allocator_crate(&mut self) { // Make sure that we actually need an allocator, if none of our // dependencies need one then we definitely don't! @@ -641,8 +721,9 @@ impl<'a> CrateReader<'a> { self.cstore.iter_crate_data(|cnum, data| { needs_allocator = needs_allocator || data.needs_allocator(); if data.is_allocator() { - debug!("{} required by rlib and is an allocator", data.name()); - self.inject_allocator_dependency(cnum); + info!("{} required by rlib and is an allocator", data.name()); + self.inject_dependency_if(cnum, "an allocator", + &|data| data.needs_allocator()); found_required_allocator = found_required_allocator || data.explicitly_linked.get(); } @@ -692,58 +773,68 @@ impl<'a> CrateReader<'a> { codemap::DUMMY_SP, PathKind::Crate, false); - // To ensure that the `-Z allocation-crate=foo` option isn't abused, and - // to ensure that the allocator is indeed an allocator, we verify that - // the crate loaded here is indeed tagged #![allocator]. + // Sanity check the crate we loaded to ensure that it is indeed an + // allocator. if !data.is_allocator() { self.sess.err(&format!("the allocator crate `{}` is not tagged \ with #![allocator]", data.name())); } self.sess.injected_allocator.set(Some(cnum)); - self.inject_allocator_dependency(cnum); + self.inject_dependency_if(cnum, "an allocator", + &|data| data.needs_allocator()); } - fn inject_allocator_dependency(&self, allocator: ast::CrateNum) { + fn inject_dependency_if(&self, + krate: ast::CrateNum, + what: &str, + needs_dep: &Fn(&cstore::crate_metadata) -> bool) { + // don't perform this validation if the session has errors, as one of + // those errors may indicate a circular dependency which could cause + // this to stack overflow. + if self.sess.has_errors() { + return + } + // Before we inject any dependencies, make sure we don't inject a - // circular dependency by validating that this allocator crate doesn't - // transitively depend on any `#![needs_allocator]` crates. - validate(self, allocator, allocator); - - // All crates tagged with `needs_allocator` do not explicitly depend on - // the allocator selected for this compile, but in order for this - // compilation to be successfully linked we need to inject a dependency - // (to order the crates on the command line correctly). - // - // Here we inject a dependency from all crates with #![needs_allocator] - // to the crate tagged with #![allocator] for this compilation unit. + // circular dependency by validating that this crate doesn't + // transitively depend on any crates satisfying `needs_dep`. + validate(self, krate, krate, what, needs_dep); + + // All crates satisfying `needs_dep` do not explicitly depend on the + // crate provided for this compile, but in order for this compilation to + // be successfully linked we need to inject a dependency (to order the + // crates on the command line correctly). self.cstore.iter_crate_data(|cnum, data| { - if !data.needs_allocator() { + if !needs_dep(data) { return } - info!("injecting a dep from {} to {}", cnum, allocator); + info!("injecting a dep from {} to {}", cnum, krate); let mut cnum_map = data.cnum_map.borrow_mut(); let remote_cnum = cnum_map.len() + 1; - let prev = cnum_map.insert(remote_cnum as ast::CrateNum, allocator); + let prev = cnum_map.insert(remote_cnum as ast::CrateNum, krate); assert!(prev.is_none()); }); - fn validate(me: &CrateReader, krate: ast::CrateNum, - allocator: ast::CrateNum) { + fn validate(me: &CrateReader, + krate: ast::CrateNum, + root: ast::CrateNum, + what: &str, + needs_dep: &Fn(&cstore::crate_metadata) -> bool) { let data = me.cstore.get_crate_data(krate); - if data.needs_allocator() { + if needs_dep(&data) { let krate_name = data.name(); - let data = me.cstore.get_crate_data(allocator); - let alloc_name = data.name(); - me.sess.err(&format!("the allocator crate `{}` cannot depend \ - on a crate that needs an allocator, but \ - it depends on `{}`", alloc_name, + let data = me.cstore.get_crate_data(root); + let root_name = data.name(); + me.sess.err(&format!("the crate `{}` cannot depend \ + on a crate that needs {}, but \ + it depends on `{}`", root_name, what, krate_name)); } for (_, &dep) in data.cnum_map.borrow().iter() { - validate(me, dep, allocator); + validate(me, dep, root, what, needs_dep); } } } @@ -774,6 +865,7 @@ impl<'a> LocalCrateReader<'a> { self.process_crate(self.krate); visit::walk_crate(self, self.krate); self.creader.inject_allocator_crate(); + self.creader.inject_panic_runtime(self.krate); if log_enabled!(log::INFO) { dump_crates(&self.cstore); diff --git a/src/librustc_metadata/csearch.rs b/src/librustc_metadata/csearch.rs index 6a79d5df80ae2..e4a0731be559a 100644 --- a/src/librustc_metadata/csearch.rs +++ b/src/librustc_metadata/csearch.rs @@ -24,6 +24,7 @@ use rustc::hir::map as hir_map; use rustc::mir::repr::Mir; use rustc::mir::mir_map::MirMap; use rustc::util::nodemap::{FnvHashMap, NodeMap, NodeSet, DefIdMap}; +use rustc::session::config::PanicStrategy; use std::cell::RefCell; use std::rc::Rc; @@ -306,6 +307,15 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore { self.get_crate_data(cnum).is_allocator() } + fn is_panic_runtime(&self, cnum: ast::CrateNum) -> bool + { + self.get_crate_data(cnum).is_panic_runtime() + } + + fn panic_strategy(&self, cnum: ast::CrateNum) -> PanicStrategy { + self.get_crate_data(cnum).panic_strategy() + } + fn crate_attrs(&self, cnum: ast::CrateNum) -> Vec { decoder::get_crate_attributes(self.get_crate_data(cnum).data()) diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index d5a9adafe7dca..04b6e1c42b98a 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -23,6 +23,7 @@ use loader; use rustc::hir::def_id::DefId; use rustc::hir::svh::Svh; use rustc::middle::cstore::{ExternCrate}; +use rustc::session::config::PanicStrategy; use rustc::util::nodemap::{FnvHashMap, NodeMap, NodeSet, DefIdMap}; use std::cell::{RefCell, Ref, Cell}; @@ -281,6 +282,20 @@ impl crate_metadata { let attrs = decoder::get_crate_attributes(self.data()); attr::contains_name(&attrs, "needs_allocator") } + + pub fn is_panic_runtime(&self) -> bool { + let attrs = decoder::get_crate_attributes(self.data()); + attr::contains_name(&attrs, "panic_runtime") + } + + pub fn needs_panic_runtime(&self) -> bool { + let attrs = decoder::get_crate_attributes(self.data()); + attr::contains_name(&attrs, "needs_panic_runtime") + } + + pub fn panic_strategy(&self) -> PanicStrategy { + decoder::get_panic_strategy(self.data()) + } } impl MetadataBlob { diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index dd5a643edc1eb..72fbbf8051533 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -27,6 +27,7 @@ use rustc::hir::svh::Svh; use rustc::hir::map as hir_map; use rustc::util::nodemap::FnvHashMap; use rustc::hir; +use rustc::session::config::PanicStrategy; use middle::cstore::{LOCAL_CRATE, FoundAst, InlinedItem, LinkagePreference}; use middle::cstore::{DefLike, DlDef, DlField, DlImpl, tls}; @@ -1760,3 +1761,13 @@ pub fn def_path(cdata: Cmd, id: DefIndex) -> hir_map::DefPath { debug!("def_path(id={:?})", id); hir_map::DefPath::make(cdata.cnum, id, |parent| def_key(cdata, parent)) } + +pub fn get_panic_strategy(data: &[u8]) -> PanicStrategy { + let crate_doc = rbml::Doc::new(data); + let strat_doc = reader::get_doc(crate_doc, tag_panic_strategy); + match reader::doc_as_u8(strat_doc) { + b'U' => PanicStrategy::Unwind, + b'A' => PanicStrategy::Abort, + b => panic!("unknown panic strategy in metadata: {}", b), + } +} diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 7558e0774b362..4a0c3bbf18702 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -33,7 +33,7 @@ use rustc::ty::util::IntTypeExt; use rustc::hir::svh::Svh; use rustc::mir::mir_map::MirMap; -use rustc::session::config; +use rustc::session::config::{self, PanicStrategy}; use rustc::util::nodemap::{FnvHashMap, NodeMap, NodeSet}; use rustc_serialize::Encodable; @@ -1828,6 +1828,17 @@ fn encode_dylib_dependency_formats(rbml_w: &mut Encoder, ecx: &EncodeContext) { } } +fn encode_panic_strategy(rbml_w: &mut Encoder, ecx: &EncodeContext) { + match ecx.tcx.sess.opts.cg.panic { + PanicStrategy::Unwind => { + rbml_w.wr_tagged_u8(tag_panic_strategy, b'U'); + } + PanicStrategy::Abort => { + rbml_w.wr_tagged_u8(tag_panic_strategy, b'A'); + } + } +} + // NB: Increment this as you change the metadata encoding version. #[allow(non_upper_case_globals)] pub const metadata_encoding_version : &'static [u8] = &[b'r', b'u', b's', b't', 0, 0, 0, 2 ]; @@ -1915,6 +1926,7 @@ fn encode_metadata_inner(rbml_w: &mut Encoder, encode_hash(rbml_w, &ecx.link_meta.crate_hash); encode_crate_disambiguator(rbml_w, &ecx.tcx.sess.crate_disambiguator.get().as_str()); encode_dylib_dependency_formats(rbml_w, &ecx); + encode_panic_strategy(rbml_w, &ecx); let mut i = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap(); encode_attributes(rbml_w, &krate.attrs); diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index dd453bf996916..cbb4be5b30022 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -1829,7 +1829,9 @@ pub fn trans_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } let _icx = push_ctxt("trans_closure"); - attributes::emit_uwtable(llfndecl, true); + if !ccx.sess().no_landing_pads() { + attributes::emit_uwtable(llfndecl, true); + } debug!("trans_closure(..., {})", instance); diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index 29bd28b6160cb..6d33fb311cff0 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -14,11 +14,14 @@ test = false alloc = { path = "../liballoc" } alloc_jemalloc = { path = "../liballoc_jemalloc", optional = true } alloc_system = { path = "../liballoc_system" } +panic_unwind = { path = "../libpanic_unwind" } +panic_abort = { path = "../libpanic_abort" } collections = { path = "../libcollections" } core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } rand = { path = "../librand" } rustc_unicode = { path = "../librustc_unicode" } +unwind = { path = "../libunwind" } [build-dependencies] build_helper = { path = "../build_helper" } diff --git a/src/libstd/build.rs b/src/libstd/build.rs index c32bca82bd5a0..e879d440643f9 100644 --- a/src/libstd/build.rs +++ b/src/libstd/build.rs @@ -28,9 +28,7 @@ fn main() { } if target.contains("linux") { - if target.contains("musl") && (target.contains("x86_64") || target.contains("i686")) { - println!("cargo:rustc-link-lib=static=unwind"); - } else if target.contains("android") { + if target.contains("android") { println!("cargo:rustc-link-lib=dl"); println!("cargo:rustc-link-lib=log"); println!("cargo:rustc-link-lib=gcc"); @@ -38,27 +36,13 @@ fn main() { println!("cargo:rustc-link-lib=dl"); println!("cargo:rustc-link-lib=rt"); println!("cargo:rustc-link-lib=pthread"); - println!("cargo:rustc-link-lib=gcc_s"); } } else if target.contains("freebsd") { println!("cargo:rustc-link-lib=execinfo"); println!("cargo:rustc-link-lib=pthread"); - println!("cargo:rustc-link-lib=gcc_s"); } else if target.contains("dragonfly") || target.contains("bitrig") || target.contains("netbsd") || target.contains("openbsd") { println!("cargo:rustc-link-lib=pthread"); - - if target.contains("rumprun") { - println!("cargo:rustc-link-lib=unwind"); - } else if target.contains("netbsd") { - println!("cargo:rustc-link-lib=gcc_s"); - } else if target.contains("openbsd") { - println!("cargo:rustc-link-lib=gcc"); - } else if target.contains("bitrig") { - println!("cargo:rustc-link-lib=c++abi"); - } else if target.contains("dragonfly") { - println!("cargo:rustc-link-lib=gcc_pic"); - } } else if target.contains("apple-darwin") { println!("cargo:rustc-link-lib=System"); } else if target.contains("apple-ios") { @@ -67,9 +51,6 @@ fn main() { println!("cargo:rustc-link-lib=framework=Security"); println!("cargo:rustc-link-lib=framework=Foundation"); } else if target.contains("windows") { - if target.contains("windows-gnu") { - println!("cargo:rustc-link-lib=gcc_eh"); - } println!("cargo:rustc-link-lib=advapi32"); println!("cargo:rustc-link-lib=ws2_32"); println!("cargo:rustc-link-lib=userenv"); diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index d4b40b844fce5..8f41bdf39e97a 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -245,6 +245,7 @@ #![feature(on_unimplemented)] #![feature(oom)] #![feature(optin_builtin_traits)] +#![feature(panic_unwind)] #![feature(placement_in_syntax)] #![feature(rand)] #![feature(raw)] @@ -283,6 +284,13 @@ #![allow(unused_features)] // std may use features in a platform-specific way #![cfg_attr(not(stage0), deny(warnings))] +// FIXME(stage0): after a snapshot, move needs_panic_runtime up above and remove +// this `extern crate` declaration and feature(panic_unwind) +#![cfg_attr(not(stage0), needs_panic_runtime)] +#![cfg_attr(not(stage0), feature(needs_panic_runtime))] +#[cfg(stage0)] +extern crate panic_unwind as __please_just_link_me_dont_reference_me; + #[cfg(test)] extern crate test; // We want to reexport a few macros from core but libcore has already been @@ -301,6 +309,9 @@ extern crate alloc; extern crate rustc_unicode; extern crate libc; +// We always need an unwinder currently for backtraces +extern crate unwind; + #[cfg(stage0)] extern crate alloc_system; diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index 39adda1066a11..d69789cedaf2c 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -17,9 +17,9 @@ /// The entry point for panic of Rust threads. /// /// This macro is used to inject panic into a Rust thread, causing the thread to -/// unwind and panic entirely. Each thread's panic can be reaped as the -/// `Box` type, and the single-argument form of the `panic!` macro will be -/// the value which is transmitted. +/// panic entirely. Each thread's panic can be reaped as the `Box` type, +/// and the single-argument form of the `panic!` macro will be the value which +/// is transmitted. /// /// The multi-argument form of this macro panics with a string and has the /// `format!` syntax for building a string. @@ -41,14 +41,14 @@ macro_rules! panic { panic!("explicit panic") }); ($msg:expr) => ({ - $crate::rt::begin_unwind($msg, { + $crate::rt::begin_panic($msg, { // static requires less code at runtime, more constant data static _FILE_LINE: (&'static str, u32) = (file!(), line!()); &_FILE_LINE }) }); ($fmt:expr, $($arg:tt)+) => ({ - $crate::rt::begin_unwind_fmt(format_args!($fmt, $($arg)+), { + $crate::rt::begin_panic_fmt(&format_args!($fmt, $($arg)+), { // The leading _'s are to avoid dead code warnings if this is // used inside a dead function. Just `#[allow(dead_code)]` is // insufficient, since the user may have diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index bbd89af01a73a..9a195d93afde4 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -16,10 +16,10 @@ use any::Any; use boxed::Box; use cell::UnsafeCell; use ops::{Deref, DerefMut}; +use panicking; use ptr::{Unique, Shared}; use rc::Rc; use sync::{Arc, Mutex, RwLock}; -use sys_common::unwind; use thread::Result; #[unstable(feature = "panic_handler", issue = "30449")] @@ -383,12 +383,9 @@ impl R> FnOnce<()> for AssertRecoverSafe { /// ``` #[stable(feature = "catch_unwind", since = "1.9.0")] pub fn catch_unwind R + UnwindSafe, R>(f: F) -> Result { - let mut result = None; unsafe { - let result = &mut result; - unwind::try(move || *result = Some(f()))? + panicking::try(f) } - Ok(result.unwrap()) } /// Deprecated, renamed to `catch_unwind` @@ -425,7 +422,7 @@ pub fn recover R + UnwindSafe, R>(f: F) -> Result { /// ``` #[stable(feature = "resume_unwind", since = "1.9.0")] pub fn resume_unwind(payload: Box) -> ! { - unwind::rust_panic(payload) + panicking::rust_panic(payload) } /// Deprecated, use resume_unwind instead diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index fd6a15b0f69a3..fd74651bf77bb 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -8,13 +8,25 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! Implementation of various bits and pieces of the `panic!` macro and +//! associated runtime pieces. +//! +//! Specifically, this module contains the implementation of: +//! +//! * Panic hooks +//! * Executing a panic up to doing the actual implementation +//! * Shims around "try" + use prelude::v1::*; use io::prelude::*; use any::Any; use cell::Cell; use cell::RefCell; +use fmt; use intrinsics; +use mem; +use raw; use sync::StaticRwLock; use sync::atomic::{AtomicBool, Ordering}; use sys::stdio::Stderr; @@ -23,14 +35,33 @@ use sys_common::thread_info; use sys_common::util; use thread; -thread_local! { pub static PANIC_COUNT: Cell = Cell::new(0) } - thread_local! { pub static LOCAL_STDERR: RefCell>> = { RefCell::new(None) } } +thread_local! { pub static PANIC_COUNT: Cell = Cell::new(0) } + +// Binary interface to the panic runtime that the standard library depends on. +// +// The standard library is tagged with `#![needs_panic_runtime]` (introduced in +// RFC 1513) to indicate that it requires some other crate tagged with +// `#![panic_runtime]` to exist somewhere. Each panic runtime is intended to +// implement these symbols (with the same signatures) so we can get matched up +// to them. +// +// One day this may look a little less ad-hoc with the compiler helping out to +// hook up these functions, but it is not this day! +extern { + fn __rust_maybe_catch_panic(f: fn(*mut u8), + data: *mut u8, + data_ptr: *mut usize, + vtable_ptr: *mut usize) -> u32; + #[unwind] + fn __rust_start_panic(data: usize, vtable: usize) -> u32; +} + #[derive(Copy, Clone)] enum Hook { Default, @@ -57,7 +88,7 @@ static FIRST_PANIC: AtomicBool = AtomicBool::new(true); /// # Panics /// /// Panics if called from a panicking thread. -#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +#[unstable(feature = "panic_handler", issue = "30449")] pub fn set_hook(hook: Box) { if thread::panicking() { panic!("cannot modify the panic hook from a panicking thread"); @@ -82,7 +113,7 @@ pub fn set_hook(hook: Box) { /// # Panics /// /// Panics if called from a panicking thread. -#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +#[unstable(feature = "panic_handler", issue = "30449")] pub fn take_hook() -> Box { if thread::panicking() { panic!("cannot modify the panic hook from a panicking thread"); @@ -102,7 +133,7 @@ pub fn take_hook() -> Box { } /// A struct providing information about a panic. -#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +#[unstable(feature = "panic_handler", issue = "30449")] pub struct PanicInfo<'a> { payload: &'a (Any + Send), location: Location<'a>, @@ -112,7 +143,7 @@ impl<'a> PanicInfo<'a> { /// Returns the payload associated with the panic. /// /// This will commonly, but not always, be a `&'static str` or `String`. - #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + #[unstable(feature = "panic_handler", issue = "30449")] pub fn payload(&self) -> &(Any + Send) { self.payload } @@ -122,14 +153,14 @@ impl<'a> PanicInfo<'a> { /// /// This method will currently always return `Some`, but this may change /// in future versions. - #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + #[unstable(feature = "panic_handler", issue = "30449")] pub fn location(&self) -> Option<&Location> { Some(&self.location) } } /// A struct containing information about the location of a panic. -#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +#[unstable(feature = "panic_handler", issue = "30449")] pub struct Location<'a> { file: &'a str, line: u32, @@ -137,20 +168,20 @@ pub struct Location<'a> { impl<'a> Location<'a> { /// Returns the name of the source file from which the panic originated. - #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + #[unstable(feature = "panic_handler", issue = "30449")] pub fn file(&self) -> &str { self.file } /// Returns the line number from which the panic originated. - #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + #[unstable(feature = "panic_handler", issue = "30449")] pub fn line(&self) -> u32 { self.line } } fn default_hook(info: &PanicInfo) { - let panics = PANIC_COUNT.with(|s| s.get()); + let panics = PANIC_COUNT.with(|c| c.get()); // If this is a double panic, make sure that we print a backtrace // for this panic. Otherwise only print it if logging is enabled. @@ -195,33 +226,143 @@ fn default_hook(info: &PanicInfo) { } } -pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) { - let panics = PANIC_COUNT.with(|s| { - let count = s.get() + 1; - s.set(count); - count +/// Invoke a closure, capturing the cause of an unwinding panic if one occurs. +pub unsafe fn try R>(f: F) -> Result> { + let mut slot = None; + let mut f = Some(f); + let ret = PANIC_COUNT.with(|s| { + let prev = s.get(); + s.set(0); + + let mut to_run = || { + slot = Some(f.take().unwrap()()); + }; + let fnptr = get_call(&mut to_run); + let dataptr = &mut to_run as *mut _ as *mut u8; + let mut any_data = 0; + let mut any_vtable = 0; + let fnptr = mem::transmute::(fnptr); + let r = __rust_maybe_catch_panic(fnptr, + dataptr, + &mut any_data, + &mut any_vtable); + s.set(prev); + + if r == 0 { + Ok(()) + } else { + Err(mem::transmute(raw::TraitObject { + data: any_data as *mut _, + vtable: any_vtable as *mut _, + })) + } + }); + + return ret.map(|()| { + slot.take().unwrap() }); - // If this is the third nested call, on_panic triggered the last panic, - // otherwise the double-panic check would have aborted the process. - // Even if it is likely that on_panic was unable to log the backtrace, - // abort immediately to avoid infinite recursion, so that attaching a - // debugger provides a useable stacktrace. - if panics >= 3 { + fn get_call(_: &mut F) -> fn(&mut F) { + call + } + + fn call(f: &mut F) { + f() + } +} + +/// Determines whether the current thread is unwinding because of panic. +pub fn panicking() -> bool { + PANIC_COUNT.with(|c| c.get() != 0) +} + +/// Entry point of panic from the libcore crate. +#[cfg(not(test))] +#[lang = "panic_fmt"] +#[unwind] +pub extern fn rust_begin_panic(msg: fmt::Arguments, + file: &'static str, + line: u32) -> ! { + begin_panic_fmt(&msg, &(file, line)) +} + +/// The entry point for panicking 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. +#[unstable(feature = "libstd_sys_internals", + reason = "used by the panic! macro", + issue = "0")] +#[inline(never)] #[cold] +pub fn begin_panic_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_panic + // below). + + let mut s = String::new(); + let _ = s.write_fmt(*msg); + begin_panic(s, file_line) +} + +/// This is the entry point of panicking for panic!() and assert!(). +#[unstable(feature = "libstd_sys_internals", + reason = "used by the panic! macro", + issue = "0")] +#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible +pub fn begin_panic(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. + + rust_panic_with_hook(Box::new(msg), file_line) +} + +/// Executes the primary logic for a panic, including checking for recursive +/// panics and panic hooks. +/// +/// This is the entry point or panics from libcore, formatted panics, and +/// `Box` panics. Here we'll verify that we're not panicking recursively, +/// run panic hooks, and then delegate to the actual implementation of panics. +#[inline(never)] +#[cold] +fn rust_panic_with_hook(msg: Box, + file_line: &(&'static str, u32)) -> ! { + let (file, line) = *file_line; + + let panics = PANIC_COUNT.with(|c| { + let prev = c.get(); + c.set(prev + 1); + prev + }); + + // If this is the third nested call (e.g. panics == 2, this is 0-indexed), + // the panic hook probably triggered the last panic, otherwise the + // double-panic check would have aborted the process. In this case abort the + // process real quickly as we don't want to try calling it again as it'll + // probably just panic again. + if panics > 1 { util::dumb_print(format_args!("thread panicked while processing \ panic. aborting.\n")); unsafe { intrinsics::abort() } } - let info = PanicInfo { - payload: obj, - location: Location { - file: file, - line: line, - }, - }; - unsafe { + let info = PanicInfo { + payload: &*msg, + location: Location { + file: file, + line: line, + }, + }; let _lock = HOOK_LOCK.read(); match HOOK { Hook::Default => default_hook(&info), @@ -229,7 +370,7 @@ pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) { } } - if panics >= 2 { + if panics > 0 { // 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 @@ -238,4 +379,17 @@ pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) { aborting.\n")); unsafe { intrinsics::abort() } } + + rust_panic(msg) +} + +/// A private no-mangle function on which to slap yer breakpoints. +#[no_mangle] +#[allow(private_no_mangle_fns)] // yes we get it, but we like breakpoints +pub fn rust_panic(msg: Box) -> ! { + let code = unsafe { + let obj = mem::transmute::<_, raw::TraitObject>(msg); + __rust_start_panic(obj.data as usize, obj.vtable as usize) + }; + rtabort!("failed to initiate panic, error {}", code) } diff --git a/src/libstd/rt.rs b/src/libstd/rt.rs index 83091c72c0d6b..6eee4ee9bbe5f 100644 --- a/src/libstd/rt.rs +++ b/src/libstd/rt.rs @@ -25,12 +25,10 @@ // Reexport some of our utilities which are expected by other crates. -pub use sys_common::unwind::{begin_unwind, begin_unwind_fmt}; +pub use panicking::{begin_panic, begin_panic_fmt}; -// Rust runtime's startup objects depend on these symbols, so they must be public. -// Since sys_common isn't public, we have to re-export them here. -#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] -pub use sys_common::unwind::imp::eh_frame_registry::*; +#[cfg(stage0)] +pub use panicking::begin_panic as begin_unwind; #[cfg(not(test))] #[lang = "start"] diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs index 56628a4c7545e..c9279883ae59d 100644 --- a/src/libstd/sys/common/mod.rs +++ b/src/libstd/sys/common/mod.rs @@ -30,9 +30,7 @@ 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 poison; @@ -41,7 +39,6 @@ pub mod rwlock; pub mod thread; pub mod thread_info; pub mod thread_local; -pub mod unwind; pub mod util; pub mod wtf8; diff --git a/src/libstd/sys/common/unwind/mod.rs b/src/libstd/sys/common/unwind/mod.rs deleted file mode 100644 index 527c2e63030d5..0000000000000 --- a/src/libstd/sys/common/unwind/mod.rs +++ /dev/null @@ -1,241 +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, the unwinder invokes each personality routine again. -//! This time it decides which (if any) cleanup code needs to be run for -//! the current stack frame. If so, the control is transferred to a special branch -//! in the function body, the "landing pad", which invokes destructors, frees memory, -//! etc. At the end of the landing pad, control is transferred back to the unwinder -//! and unwinding resumes. -//! -//! Once stack has been unwound down to the handler frame level, unwinding stops -//! and the last personality routine transfers control to the catch block. -//! -//! ## `eh_personality` and `eh_unwind_resume` -//! -//! These language items are used by the compiler when generating unwind info. -//! The first one is the personality routine described above. The second one -//! allows compilation target to customize the process of resuming unwind at the -//! end of the landing pads. `eh_unwind_resume` is used only if `custom_unwind_resume` -//! flag in the target options is set. -//! -//! ## Frame unwind info registration -//! -//! Each module's image contains a frame unwind info section (usually ".eh_frame"). -//! When a module is loaded/unloaded into the process, the unwinder must be informed -//! about the location of this section in memory. The methods of achieving that vary -//! by the platform. -//! On some (e.g. Linux), the unwinder can discover unwind info sections on its own -//! (by dynamically enumerating currently loaded modules via the dl_iterate_phdr() API -//! and finding their ".eh_frame" sections); -//! Others, like Windows, require modules to actively register their unwind info -//! sections via unwinder API (see `rust_eh_register_frames`/`rust_eh_unregister_frames`). - -#![allow(dead_code)] -#![allow(unused_imports)] - -use prelude::v1::*; - -use any::Any; -use boxed; -use cmp; -use panicking::{self,PANIC_COUNT}; -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. - -// *-pc-windows-msvc -#[cfg(target_env = "msvc")] -#[path = "seh.rs"] #[doc(hidden)] -pub mod imp; - -// x86_64-pc-windows-gnu -#[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))] -#[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; - -/// 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); - - fn try_fn(opt_closure: *mut u8) { - let opt_closure = opt_closure as *mut Option; - unsafe { (*opt_closure).take().unwrap()(); } - } -} - -unsafe fn inner_try(f: fn(*mut u8), data: *mut u8) - -> Result<(), Box> { - PANIC_COUNT.with(|s| { - let prev = s.get(); - s.set(0); - - // The "payload" here is a platform-specific region of memory which is - // used to transmit information about the exception being thrown from - // the point-of-throw back to this location. - // - // A pointer to this data is passed to the `try` intrinsic itself, - // allowing this function, the `try` intrinsic, imp::payload(), and - // imp::cleanup() to all work in concert to transmit this information. - // - // More information about what this pointer actually is can be found in - // each implementation as well as browsing the compiler source itself. - let mut payload = imp::payload(); - let r = intrinsics::try(f, data, &mut payload as *mut _ as *mut _); - s.set(prev); - if r == 0 { - Ok(()) - } else { - Err(imp::cleanup(payload)) - } - }) -} - -/// Determines whether the current thread is unwinding because of panic. -pub fn panicking() -> bool { - PANIC_COUNT.with(|s| s.get() != 0) -} - -// An uninlined, unmangled function upon which to slap yer breakpoints -#[inline(never)] -#[no_mangle] -#[allow(private_no_mangle_fns)] -pub fn rust_panic(cause: Box) -> ! { - unsafe { - imp::panic(cause) - } -} - -#[cfg(not(test))] -/// Entry point of panic from the libcore crate. -#[lang = "panic_fmt"] -#[unwind] -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. -#[unstable(feature = "libstd_sys_internals", - reason = "used by the panic! macro", - issue = "0")] -#[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!(). -#[unstable(feature = "libstd_sys_internals", - reason = "used by the panic! macro", - issue = "0")] -#[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)) -> ! { - let (file, line) = *file_line; - - // First, invoke the default panic handler. - panicking::on_panic(&*msg, file, line); - - // Finally, perform the unwinding. - rust_panic(msg); -} diff --git a/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs b/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs index 8d88091716676..5f7ad6918cdd2 100644 --- a/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs +++ b/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs @@ -15,6 +15,7 @@ use mem; use sync::StaticMutex; use super::super::printing::print; +use unwind as uw; #[inline(never)] // if we know this is a function call, we can skip it when // tracing @@ -102,126 +103,3 @@ pub fn write(w: &mut Write) -> io::Result<()> { uw::_URC_NO_REASON } } - -/// Unwind library interface used for backtraces -/// -/// Note that dead code is allowed as here are just bindings -/// iOS doesn't use all of them it but adding more -/// platform-specific configs pollutes the code too much -#[allow(non_camel_case_types)] -#[allow(non_snake_case)] -mod uw { - pub use self::_Unwind_Reason_Code::*; - - use libc; - - #[repr(C)] - 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 enum _Unwind_Context {} - - pub type _Unwind_Trace_Fn = - extern fn(ctx: *mut _Unwind_Context, - arg: *mut libc::c_void) -> _Unwind_Reason_Code; - - extern { - // No native _Unwind_Backtrace on iOS - #[cfg(not(all(target_os = "ios", target_arch = "arm")))] - pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, - trace_argument: *mut libc::c_void) - -> _Unwind_Reason_Code; - - // available since GCC 4.2.0, should be fine for our purpose - #[cfg(all(not(all(target_os = "android", target_arch = "arm")), - not(all(target_os = "linux", target_arch = "arm"))))] - pub fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, - ip_before_insn: *mut libc::c_int) - -> libc::uintptr_t; - - #[cfg(all(not(target_os = "android"), - not(all(target_os = "linux", target_arch = "arm"))))] - pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) - -> *mut libc::c_void; - } - - // On android, the function _Unwind_GetIP is a macro, and this is the - // expansion of the macro. This is all copy/pasted directly from the - // header file with the definition of _Unwind_GetIP. - #[cfg(any(all(target_os = "android", target_arch = "arm"), - all(target_os = "linux", target_arch = "arm")))] - pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t { - #[repr(C)] - enum _Unwind_VRS_Result { - _UVRSR_OK = 0, - _UVRSR_NOT_IMPLEMENTED = 1, - _UVRSR_FAILED = 2, - } - #[repr(C)] - enum _Unwind_VRS_RegClass { - _UVRSC_CORE = 0, - _UVRSC_VFP = 1, - _UVRSC_FPA = 2, - _UVRSC_WMMXD = 3, - _UVRSC_WMMXC = 4, - } - #[repr(C)] - enum _Unwind_VRS_DataRepresentation { - _UVRSD_UINT32 = 0, - _UVRSD_VFPX = 1, - _UVRSD_FPAX = 2, - _UVRSD_UINT64 = 3, - _UVRSD_FLOAT = 4, - _UVRSD_DOUBLE = 5, - } - - type _Unwind_Word = libc::c_uint; - extern { - fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context, - klass: _Unwind_VRS_RegClass, - word: _Unwind_Word, - repr: _Unwind_VRS_DataRepresentation, - data: *mut libc::c_void) - -> _Unwind_VRS_Result; - } - - let mut val: _Unwind_Word = 0; - let ptr = &mut val as *mut _Unwind_Word; - let _ = _Unwind_VRS_Get(ctx, _Unwind_VRS_RegClass::_UVRSC_CORE, 15, - _Unwind_VRS_DataRepresentation::_UVRSD_UINT32, - ptr as *mut libc::c_void); - (val & !1) as libc::uintptr_t - } - - // This function doesn't exist on Android or ARM/Linux, so make it same - // to _Unwind_GetIP - #[cfg(any(all(target_os = "android", target_arch = "arm"), - all(target_os = "linux", target_arch = "arm")))] - pub unsafe fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, - ip_before_insn: *mut libc::c_int) - -> libc::uintptr_t - { - *ip_before_insn = 0; - _Unwind_GetIP(ctx) - } - - // This function also doesn't exist on Android or ARM/Linux, so make it - // a no-op - #[cfg(any(target_os = "android", - all(target_os = "linux", target_arch = "arm")))] - pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) - -> *mut libc::c_void - { - pc - } -} diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index ab24b9e6fd601..2acf6485eb3da 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -277,21 +277,6 @@ pub const CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000; pub const EXCEPTION_CONTINUE_SEARCH: LONG = 0; pub const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd; pub const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15; -#[cfg(all(target_arch = "x86_64", target_env = "gnu"))] -pub const EXCEPTION_NONCONTINUABLE: DWORD = 0x1; // Noncontinuable exception -#[cfg(all(target_arch = "x86_64", target_env = "gnu"))] -pub const EXCEPTION_UNWINDING: DWORD = 0x2; // Unwind is in progress -#[cfg(all(target_arch = "x86_64", target_env = "gnu"))] -pub const EXCEPTION_EXIT_UNWIND: DWORD = 0x4; // Exit unwind is in progress -#[cfg(all(target_arch = "x86_64", target_env = "gnu"))] -pub const EXCEPTION_TARGET_UNWIND: DWORD = 0x20; // Target unwind in progress -#[cfg(all(target_arch = "x86_64", target_env = "gnu"))] -pub const EXCEPTION_COLLIDED_UNWIND: DWORD = 0x40; // Collided exception handler call -#[cfg(all(target_arch = "x86_64", target_env = "gnu"))] -pub const EXCEPTION_UNWIND: DWORD = EXCEPTION_UNWINDING | - EXCEPTION_EXIT_UNWIND | - EXCEPTION_TARGET_UNWIND | - EXCEPTION_COLLIDED_UNWIND; pub const PIPE_ACCESS_INBOUND: DWORD = 0x00000001; pub const FILE_FLAG_FIRST_PIPE_INSTANCE: DWORD = 0x00080000; @@ -813,31 +798,6 @@ pub struct in6_addr { pub s6_addr: [u8; 16], } -#[cfg(all(target_arch = "x86_64", target_env = "gnu"))] -pub enum UNWIND_HISTORY_TABLE {} - -#[repr(C)] -#[cfg(all(target_arch = "x86_64", target_env = "gnu"))] -pub struct RUNTIME_FUNCTION { - pub BeginAddress: DWORD, - pub EndAddress: DWORD, - pub UnwindData: DWORD, -} - -#[repr(C)] -#[cfg(all(target_arch = "x86_64", target_env = "gnu"))] -pub struct DISPATCHER_CONTEXT { - pub ControlPc: LPVOID, - pub ImageBase: LPVOID, - pub FunctionEntry: *const RUNTIME_FUNCTION, - pub EstablisherFrame: LPVOID, - pub TargetIp: LPVOID, - pub ContextRecord: *const CONTEXT, - pub LanguageHandler: LPVOID, - pub HandlerData: *const u8, - pub HistoryTable: *const UNWIND_HISTORY_TABLE, -} - #[repr(C)] #[derive(Copy, Clone)] #[allow(dead_code)] // we only use some variants @@ -1113,19 +1073,6 @@ extern "system" { pbBuffer: *mut BYTE) -> BOOL; pub fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL; - #[unwind] - #[cfg(any(target_arch = "x86_64", target_env = "msvc"))] - pub fn RaiseException(dwExceptionCode: DWORD, - dwExceptionFlags: DWORD, - nNumberOfArguments: DWORD, - lpArguments: *const ULONG_PTR); - #[cfg(all(target_arch = "x86_64", target_env = "gnu"))] - pub fn RtlUnwindEx(TargetFrame: LPVOID, - TargetIp: LPVOID, - ExceptionRecord: *const EXCEPTION_RECORD, - ReturnValue: LPVOID, - OriginalContext: *const CONTEXT, - HistoryTable: *const UNWIND_HISTORY_TABLE); pub fn GetSystemTimeAsFileTime(lpSystemTimeAsFileTime: LPFILETIME); pub fn CreateEventW(lpEventAttributes: LPSECURITY_ATTRIBUTES, diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 2f0dec759b3d5..99e6ba8c770b9 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -163,14 +163,15 @@ use prelude::v1::*; use any::Any; use cell::UnsafeCell; +use ffi::{CStr, CString}; use fmt; use io; +use panic; +use panicking; use str; -use ffi::{CStr, CString}; use sync::{Mutex, Condvar, Arc}; use sys::thread as imp; use sys_common::thread_info; -use sys_common::unwind; use sys_common::util; use sys_common::{AsInner, IntoInner}; use time::Duration; @@ -273,14 +274,8 @@ impl Builder { } unsafe { thread_info::set(imp::guard::current(), their_thread); - let mut output = None; - let try_result = { - let ptr = &mut output; - unwind::try(move || *ptr = Some(f())) - }; - *their_packet.get() = Some(try_result.map(|()| { - output.unwrap() - })); + let try_result = panic::catch_unwind(panic::AssertUnwindSafe(f)); + *their_packet.get() = Some(try_result); } }; @@ -337,7 +332,7 @@ pub fn yield_now() { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn panicking() -> bool { - unwind::panicking() + panicking::panicking() } /// Puts the current thread to sleep for the specified amount of time. diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 67bce440d4d48..7958162986cfc 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -755,7 +755,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { let expr_file_line_ptr = self.expr_addr_of(span, expr_file_line_tuple); self.expr_call_global( span, - self.std_path(&["rt", "begin_unwind"]), + self.std_path(&["rt", "begin_panic"]), vec!( self.expr_str(span, msg), expr_file_line_ptr)) diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 7f01821b00433..dbe4d9bb6f7c7 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -141,6 +141,8 @@ declare_features! ( (active, simd_ffi, "1.0.0", Some(27731)), (active, start, "1.0.0", Some(29633)), (active, structural_match, "1.8.0", Some(31434)), + (active, panic_runtime, "1.10.0", Some(32837)), + (active, needs_panic_runtime, "1.10.0", Some(32837)), // OIBIT specific features (active, optin_builtin_traits, "1.0.0", Some(13231)), @@ -435,6 +437,15 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat attribute is an experimental \ feature", cfg_fn!(needs_allocator))), + ("panic_runtime", Whitelisted, Gated("panic_runtime", + "the `#[panic_runtime]` attribute is \ + an experimental feature", + cfg_fn!(panic_runtime))), + ("needs_panic_runtime", Whitelisted, Gated("needs_panic_runtime", + "the `#[needs_panic_runtime]` \ + attribute is an experimental \ + feature", + cfg_fn!(needs_panic_runtime))), ("rustc_variance", Normal, Gated("rustc_attrs", "the `#[rustc_variance]` attribute \ is just used for rustc unit tests \ diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index c6a70edf2a8f2..88be3ade83922 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -41,10 +41,12 @@ #![feature(set_stdio)] #![feature(staged_api)] #![feature(question_mark)] +#![feature(panic_unwind)] extern crate getopts; extern crate term; extern crate libc; +extern crate panic_unwind; pub use self::TestFn::*; pub use self::ColorConfig::*; diff --git a/src/libunwind/Cargo.toml b/src/libunwind/Cargo.toml new file mode 100644 index 0000000000000..dca222c49d0a1 --- /dev/null +++ b/src/libunwind/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Rust Project Developers"] +name = "unwind" +version = "0.0.0" +build = "build.rs" + +[lib] +name = "unwind" +path = "lib.rs" + +[dependencies] +core = { path = "../libcore" } +libc = { path = "../rustc/libc_shim" } diff --git a/src/libunwind/build.rs b/src/libunwind/build.rs new file mode 100644 index 0000000000000..ebe6fd54799ee --- /dev/null +++ b/src/libunwind/build.rs @@ -0,0 +1,39 @@ +// Copyright 2016 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 std::env; + +fn main() { + println!("cargo:rustc-cfg=cargobuild"); + + let target = env::var("TARGET").unwrap(); + + if target.contains("linux") { + if target.contains("musl") && (target.contains("x86_64") || target.contains("i686")) { + println!("cargo:rustc-link-lib=static=unwind"); + } else if !target.contains("android") { + println!("cargo:rustc-link-lib=gcc_s"); + } + } else if target.contains("freebsd") { + println!("cargo:rustc-link-lib=gcc_s"); + } else if target.contains("rumprun") { + println!("cargo:rustc-link-lib=unwind"); + } else if target.contains("netbsd") { + println!("cargo:rustc-link-lib=gcc_s"); + } else if target.contains("openbsd") { + println!("cargo:rustc-link-lib=gcc"); + } else if target.contains("bitrig") { + println!("cargo:rustc-link-lib=c++abi"); + } else if target.contains("dragonfly") { + println!("cargo:rustc-link-lib=gcc_pic"); + } else if target.contains("windows-gnu") { + println!("cargo:rustc-link-lib=gcc_eh"); + } +} diff --git a/src/libunwind/lib.rs b/src/libunwind/lib.rs new file mode 100644 index 0000000000000..3ff61fd93d758 --- /dev/null +++ b/src/libunwind/lib.rs @@ -0,0 +1,30 @@ +// Copyright 2016 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. + +#![no_std] +#![crate_name = "unwind"] +#![crate_type = "rlib"] +#![unstable(feature = "panic_unwind", issue = "32837")] +#![cfg_attr(not(stage0), deny(warnings))] + +#![feature(cfg_target_vendor)] +#![feature(staged_api)] +#![feature(unwind_attributes)] + +#![cfg_attr(not(target_env = "msvc"), feature(libc))] + +#[cfg(not(target_env = "msvc"))] +extern crate libc; + +#[cfg(not(target_env = "msvc"))] +mod libunwind; +#[cfg(not(target_env = "msvc"))] +pub use libunwind::*; + diff --git a/src/libstd/sys/common/libunwind.rs b/src/libunwind/libunwind.rs similarity index 57% rename from src/libstd/sys/common/libunwind.rs rename to src/libunwind/libunwind.rs index c1e9782852a79..ce74e5de3d460 100644 --- a/src/libstd/sys/common/libunwind.rs +++ b/src/libunwind/libunwind.rs @@ -1,4 +1,4 @@ -// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,12 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Unwind library interface +#![allow(bad_style)] -#![allow(non_upper_case_globals)] -#![allow(non_camel_case_types)] -#![allow(non_snake_case)] -#![allow(dead_code)] // these are just bindings +use libc; #[cfg(any(not(target_arch = "arm"), target_os = "ios"))] pub use self::_Unwind_Action::*; @@ -21,11 +18,9 @@ pub use self::_Unwind_Action::*; 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)] +#[derive(Clone, Copy)] pub enum _Unwind_Action { _UA_SEARCH_PHASE = 1, _UA_CLEANUP_PHASE = 2, @@ -36,7 +31,7 @@ pub enum _Unwind_Action { #[cfg(target_arch = "arm")] #[repr(C)] -#[derive(Copy, Clone)] +#[derive(Clone, Copy)] pub enum _Unwind_State { _US_VIRTUAL_UNWIND_FRAME = 0, _US_UNWIND_FRAME_STARTING = 1, @@ -47,7 +42,6 @@ pub enum _Unwind_State { } #[repr(C)] -#[derive(Copy, Clone)] pub enum _Unwind_Reason_Code { _URC_NO_REASON = 0, _URC_FOREIGN_EXCEPTION_CAUGHT = 1, @@ -65,6 +59,10 @@ pub type _Unwind_Exception_Class = u64; pub type _Unwind_Word = libc::uintptr_t; +pub type _Unwind_Trace_Fn = + extern fn(ctx: *mut _Unwind_Context, + arg: *mut libc::c_void) -> _Unwind_Reason_Code; + #[cfg(target_arch = "x86")] pub const unwinder_private_data_size: usize = 5; @@ -126,9 +124,12 @@ pub type _Unwind_Exception_Cleanup_Fn = link(name = "gcc_pic"))] #[cfg_attr(target_os = "bitrig", link(name = "c++abi"))] -#[cfg_attr(all(target_os = "windows", target_env="gnu"), +#[cfg_attr(all(target_os = "windows", target_env = "gnu"), link(name = "gcc_eh"))] -extern "C" { +#[cfg(not(cargobuild))] +extern {} + +extern { // iOS on armv7 uses SjLj exceptions and requires to link // against corresponding routine (..._SjLj_...) #[cfg(not(all(target_os = "ios", target_arch = "arm")))] @@ -145,14 +146,102 @@ extern "C" { #[unwind] pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !; + + // No native _Unwind_Backtrace on iOS + #[cfg(not(all(target_os = "ios", target_arch = "arm")))] + pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, + trace_argument: *mut libc::c_void) + -> _Unwind_Reason_Code; + + // available since GCC 4.2.0, should be fine for our purpose + #[cfg(all(not(all(target_os = "android", target_arch = "arm")), + not(all(target_os = "linux", target_arch = "arm"))))] + pub fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, + ip_before_insn: *mut libc::c_int) + -> libc::uintptr_t; + + #[cfg(all(not(target_os = "android"), + not(all(target_os = "linux", target_arch = "arm"))))] + pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) + -> *mut libc::c_void; } // ... 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)] +#[inline] pub unsafe fn _Unwind_RaiseException(exc: *mut _Unwind_Exception) -> _Unwind_Reason_Code { _Unwind_SjLj_RaiseException(exc) } + +// On android, the function _Unwind_GetIP is a macro, and this is the +// expansion of the macro. This is all copy/pasted directly from the +// header file with the definition of _Unwind_GetIP. +#[cfg(any(all(target_os = "android", target_arch = "arm"), + all(target_os = "linux", target_arch = "arm")))] +pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t { + #[repr(C)] + enum _Unwind_VRS_Result { + _UVRSR_OK = 0, + _UVRSR_NOT_IMPLEMENTED = 1, + _UVRSR_FAILED = 2, + } + #[repr(C)] + enum _Unwind_VRS_RegClass { + _UVRSC_CORE = 0, + _UVRSC_VFP = 1, + _UVRSC_FPA = 2, + _UVRSC_WMMXD = 3, + _UVRSC_WMMXC = 4, + } + #[repr(C)] + enum _Unwind_VRS_DataRepresentation { + _UVRSD_UINT32 = 0, + _UVRSD_VFPX = 1, + _UVRSD_FPAX = 2, + _UVRSD_UINT64 = 3, + _UVRSD_FLOAT = 4, + _UVRSD_DOUBLE = 5, + } + + type _Unwind_Word = libc::c_uint; + extern { + fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context, + klass: _Unwind_VRS_RegClass, + word: _Unwind_Word, + repr: _Unwind_VRS_DataRepresentation, + data: *mut libc::c_void) + -> _Unwind_VRS_Result; + } + + let mut val: _Unwind_Word = 0; + let ptr = &mut val as *mut _Unwind_Word; + let _ = _Unwind_VRS_Get(ctx, _Unwind_VRS_RegClass::_UVRSC_CORE, 15, + _Unwind_VRS_DataRepresentation::_UVRSD_UINT32, + ptr as *mut libc::c_void); + (val & !1) as libc::uintptr_t +} + +// This function doesn't exist on Android or ARM/Linux, so make it same +// to _Unwind_GetIP +#[cfg(any(all(target_os = "android", target_arch = "arm"), + all(target_os = "linux", target_arch = "arm")))] +pub unsafe fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, + ip_before_insn: *mut libc::c_int) + -> libc::uintptr_t +{ + *ip_before_insn = 0; + _Unwind_GetIP(ctx) +} + +// This function also doesn't exist on Android or ARM/Linux, so make it +// a no-op +#[cfg(any(target_os = "android", + all(target_os = "linux", target_arch = "arm")))] +pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) + -> *mut libc::c_void +{ + pc +} diff --git a/src/rustc/std_shim/Cargo.lock b/src/rustc/std_shim/Cargo.lock index 530c04da8a0c9..bad46966ffa6b 100644 --- a/src/rustc/std_shim/Cargo.lock +++ b/src/rustc/std_shim/Cargo.lock @@ -5,15 +5,6 @@ dependencies = [ "std 0.0.0", ] -[[package]] -name = "advapi32-sys" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "alloc" version = "0.0.0" @@ -27,7 +18,7 @@ version = "0.0.0" dependencies = [ "build_helper 0.1.0", "core 0.0.0", - "gcc 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.0.0", ] @@ -58,18 +49,32 @@ version = "0.0.0" [[package]] name = "gcc" -version = "0.3.17" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.0.0" dependencies = [ - "advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "core 0.0.0", ] [[package]] -name = "libc" +name = "panic_abort" version = "0.0.0" dependencies = [ "core 0.0.0", + "libc 0.0.0", +] + +[[package]] +name = "panic_unwind" +version = "0.0.0" +dependencies = [ + "alloc 0.0.0", + "core 0.0.0", + "libc 0.0.0", + "unwind 0.0.0", ] [[package]] @@ -96,19 +101,20 @@ dependencies = [ "build_helper 0.1.0", "collections 0.0.0", "core 0.0.0", - "gcc 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.0.0", + "panic_abort 0.0.0", + "panic_unwind 0.0.0", "rand 0.0.0", "rustc_unicode 0.0.0", + "unwind 0.0.0", ] [[package]] -name = "winapi" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "unwind" +version = "0.0.0" +dependencies = [ + "core 0.0.0", + "libc 0.0.0", +] diff --git a/src/test/codegen/lto-removes-invokes.rs b/src/test/codegen/lto-removes-invokes.rs new file mode 100644 index 0000000000000..b2f43489523d1 --- /dev/null +++ b/src/test/codegen/lto-removes-invokes.rs @@ -0,0 +1,31 @@ +// Copyright 2016 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. + +// compile-flags: -C lto -C panic=abort -O +// no-prefer-dynamic + +fn main() { + foo(); +} + +#[no_mangle] +#[inline(never)] +fn foo() { + let _a = Box::new(3); + bar(); +// CHECK-LABEL: foo +// CHECK: call {{.*}} void @bar +} + +#[inline(never)] +#[no_mangle] +fn bar() { + println!("hello!"); +} diff --git a/src/test/compile-fail/panic-runtime/abort-link-to-unwind-dylib.rs b/src/test/compile-fail/panic-runtime/abort-link-to-unwind-dylib.rs new file mode 100644 index 0000000000000..c3242a5082b62 --- /dev/null +++ b/src/test/compile-fail/panic-runtime/abort-link-to-unwind-dylib.rs @@ -0,0 +1,24 @@ +// Copyright 2016 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. + +// compile-flags:-C panic=abort -C prefer-dynamic +// ignore-musl - no dylibs here +// error-pattern:`panic_unwind` is not compiled with this crate's panic strategy + +// This is a test where the local crate, compiled with `panic=abort`, links to +// the standard library **dynamically** which is already linked against +// `panic=unwind`. We should fail because the linked panic runtime does not +// correspond with our `-C panic` option. +// +// Note that this test assumes that the dynamic version of the standard library +// is linked to `panic_unwind`, which is currently the case. + +fn main() { +} diff --git a/src/test/compile-fail/panic-runtime/auxiliary/needs-panic-runtime.rs b/src/test/compile-fail/panic-runtime/auxiliary/needs-panic-runtime.rs new file mode 100644 index 0000000000000..d6c21fecf6b56 --- /dev/null +++ b/src/test/compile-fail/panic-runtime/auxiliary/needs-panic-runtime.rs @@ -0,0 +1,16 @@ +// Copyright 2016 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. + +// no-prefer-dynamic + +#![feature(needs_panic_runtime)] +#![crate_type = "rlib"] +#![needs_panic_runtime] +#![no_std] diff --git a/src/test/compile-fail/panic-runtime/auxiliary/panic-runtime-abort.rs b/src/test/compile-fail/panic-runtime/auxiliary/panic-runtime-abort.rs new file mode 100644 index 0000000000000..3b74156b6b017 --- /dev/null +++ b/src/test/compile-fail/panic-runtime/auxiliary/panic-runtime-abort.rs @@ -0,0 +1,27 @@ +// Copyright 2016 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. + +// compile-flags:-C panic=abort +// no-prefer-dynamic + +#![feature(panic_runtime)] +#![crate_type = "rlib"] + +#![no_std] +#![panic_runtime] + +#[no_mangle] +pub extern fn __rust_maybe_catch_panic() {} + +#[no_mangle] +pub extern fn __rust_start_panic() {} + +#[no_mangle] +pub extern fn rust_eh_personality() {} diff --git a/src/test/compile-fail/panic-runtime/auxiliary/panic-runtime-lang-items.rs b/src/test/compile-fail/panic-runtime/auxiliary/panic-runtime-lang-items.rs new file mode 100644 index 0000000000000..fbf70b3d3fefe --- /dev/null +++ b/src/test/compile-fail/panic-runtime/auxiliary/panic-runtime-lang-items.rs @@ -0,0 +1,23 @@ +// Copyright 2016 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. + +// no-prefer-dynamic + +#![crate_type = "rlib"] + +#![no_std] +#![feature(lang_items)] + +#[lang = "panic_fmt"] +fn panic_fmt() {} +#[lang = "eh_personality"] +fn eh_personality() {} +#[lang = "eh_unwind_resume"] +fn eh_unwind_resume() {} diff --git a/src/test/compile-fail/panic-runtime/auxiliary/panic-runtime-unwind.rs b/src/test/compile-fail/panic-runtime/auxiliary/panic-runtime-unwind.rs new file mode 100644 index 0000000000000..4bb36839d988b --- /dev/null +++ b/src/test/compile-fail/panic-runtime/auxiliary/panic-runtime-unwind.rs @@ -0,0 +1,27 @@ +// Copyright 2016 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. + +// compile-flags:-C panic=unwind +// no-prefer-dynamic + +#![feature(panic_runtime)] +#![crate_type = "rlib"] + +#![no_std] +#![panic_runtime] + +#[no_mangle] +pub extern fn __rust_maybe_catch_panic() {} + +#[no_mangle] +pub extern fn __rust_start_panic() {} + +#[no_mangle] +pub extern fn rust_eh_personality() {} diff --git a/src/test/compile-fail/panic-runtime/auxiliary/panic-runtime-unwind2.rs b/src/test/compile-fail/panic-runtime/auxiliary/panic-runtime-unwind2.rs new file mode 100644 index 0000000000000..4bb36839d988b --- /dev/null +++ b/src/test/compile-fail/panic-runtime/auxiliary/panic-runtime-unwind2.rs @@ -0,0 +1,27 @@ +// Copyright 2016 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. + +// compile-flags:-C panic=unwind +// no-prefer-dynamic + +#![feature(panic_runtime)] +#![crate_type = "rlib"] + +#![no_std] +#![panic_runtime] + +#[no_mangle] +pub extern fn __rust_maybe_catch_panic() {} + +#[no_mangle] +pub extern fn __rust_start_panic() {} + +#[no_mangle] +pub extern fn rust_eh_personality() {} diff --git a/src/test/compile-fail/panic-runtime/auxiliary/runtime-depending-on-panic-runtime.rs b/src/test/compile-fail/panic-runtime/auxiliary/runtime-depending-on-panic-runtime.rs new file mode 100644 index 0000000000000..b90dec9281bc1 --- /dev/null +++ b/src/test/compile-fail/panic-runtime/auxiliary/runtime-depending-on-panic-runtime.rs @@ -0,0 +1,18 @@ +// Copyright 2016 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. + +// no-prefer-dynamic + +#![feature(panic_runtime)] +#![crate_type = "rlib"] +#![panic_runtime] +#![no_std] + +extern crate needs_panic_runtime; diff --git a/src/test/compile-fail/panic-runtime/auxiliary/wants-panic-runtime-abort.rs b/src/test/compile-fail/panic-runtime/auxiliary/wants-panic-runtime-abort.rs new file mode 100644 index 0000000000000..e1902e44a60e2 --- /dev/null +++ b/src/test/compile-fail/panic-runtime/auxiliary/wants-panic-runtime-abort.rs @@ -0,0 +1,17 @@ +// Copyright 2016 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. + +// compile-flags:-C panic=abort +// no-prefer-dynamic + +#![crate_type = "rlib"] +#![no_std] + +extern crate panic_runtime_abort; diff --git a/src/test/compile-fail/panic-runtime/auxiliary/wants-panic-runtime-unwind.rs b/src/test/compile-fail/panic-runtime/auxiliary/wants-panic-runtime-unwind.rs new file mode 100644 index 0000000000000..2183338b24985 --- /dev/null +++ b/src/test/compile-fail/panic-runtime/auxiliary/wants-panic-runtime-unwind.rs @@ -0,0 +1,16 @@ +// Copyright 2016 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. + +// no-prefer-dynamic + +#![crate_type = "rlib"] +#![no_std] + +extern crate panic_runtime_unwind; diff --git a/src/test/compile-fail/panic-runtime/bad-panic-flag1.rs b/src/test/compile-fail/panic-runtime/bad-panic-flag1.rs new file mode 100644 index 0000000000000..f067b6b8349b6 --- /dev/null +++ b/src/test/compile-fail/panic-runtime/bad-panic-flag1.rs @@ -0,0 +1,14 @@ +// Copyright 2016 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. + +// compile-flags:-C panic=foo +// error-pattern:either `panic` or `abort` was expected + +fn main() {} diff --git a/src/test/compile-fail/panic-runtime/bad-panic-flag2.rs b/src/test/compile-fail/panic-runtime/bad-panic-flag2.rs new file mode 100644 index 0000000000000..0ecf65f080fa9 --- /dev/null +++ b/src/test/compile-fail/panic-runtime/bad-panic-flag2.rs @@ -0,0 +1,14 @@ +// Copyright 2016 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. + +// compile-flags:-C panic +// error-pattern:requires either `panic` or `abort` + +fn main() {} diff --git a/src/test/compile-fail/panic-runtime/libtest-unwinds.rs b/src/test/compile-fail/panic-runtime/libtest-unwinds.rs new file mode 100644 index 0000000000000..5f6f4ecbd6f6e --- /dev/null +++ b/src/test/compile-fail/panic-runtime/libtest-unwinds.rs @@ -0,0 +1,20 @@ +// Copyright 2016 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. + +// error-pattern:is not compiled with this crate's panic strategy `abort` +// compile-flags:-C panic=abort + +#![feature(test)] + +extern crate test; + +fn main() { +} + diff --git a/src/test/compile-fail/panic-runtime/needs-gate.rs b/src/test/compile-fail/panic-runtime/needs-gate.rs new file mode 100644 index 0000000000000..02f3da58f1da8 --- /dev/null +++ b/src/test/compile-fail/panic-runtime/needs-gate.rs @@ -0,0 +1,14 @@ +// Copyright 2016 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. + +#![panic_runtime] //~ ERROR: is an experimental feature +#![needs_panic_runtime] //~ ERROR: is an experimental feature + +fn main() {} diff --git a/src/test/compile-fail/panic-runtime/runtime-depend-on-needs-runtime.rs b/src/test/compile-fail/panic-runtime/runtime-depend-on-needs-runtime.rs new file mode 100644 index 0000000000000..0681f991067b1 --- /dev/null +++ b/src/test/compile-fail/panic-runtime/runtime-depend-on-needs-runtime.rs @@ -0,0 +1,15 @@ +// Copyright 2016 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. + +// aux-build:needs-panic-runtime.rs +// aux-build:runtime-depending-on-panic-runtime.rs +// error-pattern:cannot depend on a crate that needs a panic runtime + +extern crate runtime_depending_on_panic_runtime; diff --git a/src/test/compile-fail/panic-runtime/transitive-link-a-bunch.rs b/src/test/compile-fail/panic-runtime/transitive-link-a-bunch.rs new file mode 100644 index 0000000000000..885b3e6dbb941 --- /dev/null +++ b/src/test/compile-fail/panic-runtime/transitive-link-a-bunch.rs @@ -0,0 +1,24 @@ +// Copyright 2016 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. + +// aux-build:panic-runtime-unwind.rs +// aux-build:panic-runtime-abort.rs +// aux-build:wants-panic-runtime-unwind.rs +// aux-build:wants-panic-runtime-abort.rs +// aux-build:panic-runtime-lang-items.rs +// error-pattern: is not compiled with this crate's panic strategy `unwind` + +#![no_std] + +extern crate wants_panic_runtime_unwind; +extern crate wants_panic_runtime_abort; +extern crate panic_runtime_lang_items; + +fn main() {} diff --git a/src/test/compile-fail/panic-runtime/two-panic-runtimes.rs b/src/test/compile-fail/panic-runtime/two-panic-runtimes.rs new file mode 100644 index 0000000000000..0fe0da2fa2c57 --- /dev/null +++ b/src/test/compile-fail/panic-runtime/two-panic-runtimes.rs @@ -0,0 +1,23 @@ +// Copyright 2016 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. + +// error-pattern:cannot link together two panic runtimes: panic_runtime_unwind and panic_runtime_unwind2 +// ignore-tidy-linelength +// aux-build:panic-runtime-unwind.rs +// aux-build:panic-runtime-unwind2.rs +// aux-build:panic-runtime-lang-items.rs + +#![no_std] + +extern crate panic_runtime_unwind; +extern crate panic_runtime_unwind2; +extern crate panic_runtime_lang_items; + +fn main() {} diff --git a/src/test/compile-fail/panic-runtime/want-abort-got-unwind.rs b/src/test/compile-fail/panic-runtime/want-abort-got-unwind.rs new file mode 100644 index 0000000000000..b178006411ba0 --- /dev/null +++ b/src/test/compile-fail/panic-runtime/want-abort-got-unwind.rs @@ -0,0 +1,17 @@ +// Copyright 2016 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. + +// error-pattern:is not compiled with this crate's panic strategy `abort` +// aux-build:panic-runtime-unwind.rs +// compile-flags:-C panic=abort + +extern crate panic_runtime_unwind; + +fn main() {} diff --git a/src/test/compile-fail/panic-runtime/want-abort-got-unwind2.rs b/src/test/compile-fail/panic-runtime/want-abort-got-unwind2.rs new file mode 100644 index 0000000000000..de8e010c3cb48 --- /dev/null +++ b/src/test/compile-fail/panic-runtime/want-abort-got-unwind2.rs @@ -0,0 +1,18 @@ +// Copyright 2016 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. + +// error-pattern:is not compiled with this crate's panic strategy `abort` +// aux-build:panic-runtime-unwind.rs +// aux-build:wants-panic-runtime-unwind.rs +// compile-flags:-C panic=abort + +extern crate wants_panic_runtime_unwind; + +fn main() {} diff --git a/src/test/compile-fail/panic-runtime/want-unwind-got-abort.rs b/src/test/compile-fail/panic-runtime/want-unwind-got-abort.rs new file mode 100644 index 0000000000000..88ad36f310eb5 --- /dev/null +++ b/src/test/compile-fail/panic-runtime/want-unwind-got-abort.rs @@ -0,0 +1,20 @@ +// Copyright 2016 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. + +// error-pattern:is incompatible with this crate's strategy of `unwind` +// aux-build:panic-runtime-abort.rs +// aux-build:panic-runtime-lang-items.rs + +#![no_std] + +extern crate panic_runtime_abort; +extern crate panic_runtime_lang_items; + +fn main() {} diff --git a/src/test/compile-fail/panic-runtime/want-unwind-got-abort2.rs b/src/test/compile-fail/panic-runtime/want-unwind-got-abort2.rs new file mode 100644 index 0000000000000..c42a25a553a7f --- /dev/null +++ b/src/test/compile-fail/panic-runtime/want-unwind-got-abort2.rs @@ -0,0 +1,21 @@ +// Copyright 2016 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. + +// error-pattern:is incompatible with this crate's strategy of `unwind` +// aux-build:panic-runtime-abort.rs +// aux-build:wants-panic-runtime-abort.rs +// aux-build:panic-runtime-lang-items.rs + +#![no_std] + +extern crate wants_panic_runtime_abort; +extern crate panic_runtime_lang_items; + +fn main() {} diff --git a/src/test/run-pass/panic-runtime/abort-link-to-unwinding-crates.rs b/src/test/run-pass/panic-runtime/abort-link-to-unwinding-crates.rs new file mode 100644 index 0000000000000..71c1a61062d10 --- /dev/null +++ b/src/test/run-pass/panic-runtime/abort-link-to-unwinding-crates.rs @@ -0,0 +1,35 @@ +// Copyright 2016 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. + +// compile-flags:-C panic=abort +// aux-build:exit-success-if-unwind.rs +// no-prefer-dynamic + +extern crate exit_success_if_unwind; + +use std::process::Command; +use std::env; + +fn main() { + let mut args = env::args_os(); + let me = args.next().unwrap(); + + if let Some(s) = args.next() { + if &*s == "foo" { + exit_success_if_unwind::bar(do_panic); + } + } + let s = Command::new(env::args_os().next().unwrap()).arg("foo").status(); + assert!(s.unwrap().code() != Some(0)); +} + +fn do_panic() { + panic!("try to catch me"); +} diff --git a/src/test/run-pass/panic-runtime/abort.rs b/src/test/run-pass/panic-runtime/abort.rs new file mode 100644 index 0000000000000..2fc9d6cfd04a1 --- /dev/null +++ b/src/test/run-pass/panic-runtime/abort.rs @@ -0,0 +1,39 @@ +// Copyright 2016 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. + +// compile-flags:-C panic=abort +// no-prefer-dynamic + +use std::process::Command; +use std::env; + +struct Bomb; + +impl Drop for Bomb { + fn drop(&mut self) { + std::process::exit(0); + } +} + +fn main() { + let mut args = env::args_os(); + let me = args.next().unwrap(); + + if let Some(s) = args.next() { + if &*s == "foo" { + + let _bomb = Bomb; + + panic!("try to catch me"); + } + } + let s = Command::new(env::args_os().next().unwrap()).arg("foo").status(); + assert!(s.unwrap().code() != Some(0)); +} diff --git a/src/test/run-pass/panic-runtime/auxiliary/exit-success-if-unwind.rs b/src/test/run-pass/panic-runtime/auxiliary/exit-success-if-unwind.rs new file mode 100644 index 0000000000000..9e5fc592b1a33 --- /dev/null +++ b/src/test/run-pass/panic-runtime/auxiliary/exit-success-if-unwind.rs @@ -0,0 +1,26 @@ +// Copyright 2016 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. + +// no-prefer-dynamic + +#![crate_type = "rlib"] + +struct Bomb; + +impl Drop for Bomb { + fn drop(&mut self) { + std::process::exit(0); + } +} + +pub fn bar(f: fn()) { + let _bomb = Bomb; + f(); +} diff --git a/src/test/run-pass/panic-runtime/link-to-abort.rs b/src/test/run-pass/panic-runtime/link-to-abort.rs new file mode 100644 index 0000000000000..71e35e41fc046 --- /dev/null +++ b/src/test/run-pass/panic-runtime/link-to-abort.rs @@ -0,0 +1,19 @@ +// Copyright 2016 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. + +// compile-flags:-C panic=abort +// no-prefer-dynamic + +#![feature(panic_abort)] + +extern crate panic_abort; + +fn main() { +} diff --git a/src/test/run-pass/panic-runtime/link-to-unwind.rs b/src/test/run-pass/panic-runtime/link-to-unwind.rs new file mode 100644 index 0000000000000..dec8f738d3294 --- /dev/null +++ b/src/test/run-pass/panic-runtime/link-to-unwind.rs @@ -0,0 +1,18 @@ +// Copyright 2016 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. + +// no-prefer-dynamic + +#![feature(panic_unwind)] + +extern crate panic_unwind; + +fn main() { +} diff --git a/src/test/run-pass/panic-runtime/lto-abort.rs b/src/test/run-pass/panic-runtime/lto-abort.rs new file mode 100644 index 0000000000000..09e33b88189fe --- /dev/null +++ b/src/test/run-pass/panic-runtime/lto-abort.rs @@ -0,0 +1,39 @@ +// Copyright 2016 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. + +// compile-flags:-C lto -C panic=abort +// no-prefer-dynamic + +use std::process::Command; +use std::env; + +struct Bomb; + +impl Drop for Bomb { + fn drop(&mut self) { + std::process::exit(0); + } +} + +fn main() { + let mut args = env::args_os(); + let me = args.next().unwrap(); + + if let Some(s) = args.next() { + if &*s == "foo" { + + let _bomb = Bomb; + + panic!("try to catch me"); + } + } + let s = Command::new(env::args_os().next().unwrap()).arg("foo").status(); + assert!(s.unwrap().code() != Some(0)); +} diff --git a/src/test/run-pass/panic-runtime/lto-unwind.rs b/src/test/run-pass/panic-runtime/lto-unwind.rs new file mode 100644 index 0000000000000..10e633b3775b3 --- /dev/null +++ b/src/test/run-pass/panic-runtime/lto-unwind.rs @@ -0,0 +1,41 @@ +// Copyright 2016 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. + +// compile-flags:-C lto -C panic=unwind +// no-prefer-dynamic + +use std::process::Command; +use std::env; + +struct Bomb; + +impl Drop for Bomb { + fn drop(&mut self) { + println!("hurray you ran me"); + } +} + +fn main() { + let mut args = env::args_os(); + let me = args.next().unwrap(); + + if let Some(s) = args.next() { + if &*s == "foo" { + + let _bomb = Bomb; + + panic!("try to catch me"); + } + } + let s = Command::new(env::args_os().next().unwrap()).arg("foo").output(); + let s = s.unwrap(); + assert!(!s.status.success()); + assert!(String::from_utf8_lossy(&s.stdout).contains("hurray you ran me")); +} diff --git a/src/tools/tidy/src/cargo.rs b/src/tools/tidy/src/cargo.rs index 77dcf9c1bd81f..6a9d52cb0048f 100644 --- a/src/tools/tidy/src/cargo.rs +++ b/src/tools/tidy/src/cargo.rs @@ -85,6 +85,9 @@ fn verify(tomlfile: &Path, libfile: &Path, bad: &mut bool) { if krate == "alloc_jemalloc" && toml.contains("name = \"std\"") { continue } + if krate == "panic_abort" && toml.contains("name = \"std\"") { + continue + } if !librs.contains(&format!("extern crate {}", krate)) { println!("{} doesn't have `extern crate {}`, but Cargo.toml \