diff --git a/mk/crates.mk b/mk/crates.mk index 48c1e4ad59d5..1583515014a3 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 cc712413d3b1..ea610f63dbf2 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 99e16035d1d4..046bc34438c4 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 ca4c9bfd9544..a22299c5e1a5 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 000000000000..a7905703f596 --- /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 000000000000..c085ddeb75b9 --- /dev/null +++ b/src/libpanic_abort/lib.rs @@ -0,0 +1,135 @@ +// 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. +#[cfg(not(stage0))] +pub mod personalities { + + #[no_mangle] + #[cfg(not(all(target_os = "windows", + target_env = "gnu", + target_arch = "x86_64")))] + pub extern fn rust_eh_personality() {} + + // On x86_64-pc-windows-gnu we use our own personality function that needs + // to return `ExceptionContinueSearch` as we're passing on all our frames. + #[no_mangle] + #[cfg(all(target_os = "windows", + target_env = "gnu", + target_arch = "x86_64"))] + pub extern fn rust_eh_personality(_record: usize, + _frame: usize, + _context: usize, + _dispatcher: usize) -> u32 { + 1 // `ExceptionContinueSearch` + } + + // Similar to above, this corresponds to the `eh_unwind_resume` lang item + // that's only used on Windows currently. + // + // Note that we don't execute landing pads, so this is never called, so it's + // body is empty. + #[no_mangle] + #[cfg(all(target_os = "windows", target_env = "gnu"))] + pub extern fn rust_eh_unwind_resume() {} + + // These two are called by our startup objects on i686-pc-windows-gnu, but + // they don't need to do anything so the bodies are nops. + #[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 000000000000..20d826d4a470 --- /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 000000000000..27edecd6f966 --- /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 319be245bde9..1c3fca98a1f7 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 822826bcc837..cde21f90811d 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 da7a340af351..50b2e1534d70 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 000000000000..17cbd2e0d4c3 --- /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/libpanic_unwind/seh.rs b/src/libpanic_unwind/seh.rs new file mode 100644 index 000000000000..04a3f7b9663f --- /dev/null +++ b/src/libpanic_unwind/seh.rs @@ -0,0 +1,326 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Windows SEH +//! +//! On Windows (currently only on MSVC), the default exception handling +//! mechanism is Structured Exception Handling (SEH). This is quite different +//! than Dwarf-based exception handling (e.g. what other unix platforms use) in +//! terms of compiler internals, so LLVM is required to have a good deal of +//! extra support for SEH. +//! +//! In a nutshell, what happens here is: +//! +//! 1. The `panic` function calls the standard Windows function +//! `_CxxThrowException` to throw a C++-like exception, triggering the +//! unwinding process. +//! 2. All landing pads generated by the compiler use the personality function +//! `__CxxFrameHandler3`, a function in the CRT, and the unwinding code in +//! Windows will use this personality function to execute all cleanup code on +//! the stack. +//! 3. All compiler-generated calls to `invoke` have a landing pad set as a +//! `cleanuppad` LLVM instruction, which indicates the start of the cleanup +//! routine. The personality (in step 2, defined in the CRT) is responsible +//! for running the cleanup routines. +//! 4. Eventually the "catch" code in the `try` intrinsic (generated by the +//! compiler) is executed and indicates that control should come back to +//! Rust. This is done via a `catchswitch` plus a `catchpad` instruction in +//! LLVM IR terms, finally returning normal control to the program with a +//! `catchret` instruction. +//! +//! Some specific differences from the gcc-based exception handling are: +//! +//! * Rust has no custom personality function, it is instead *always* +//! `__CxxFrameHandler3`. Additionally, no extra filtering is performed, so we +//! end up catching any C++ exceptions that happen to look like the kind we're +//! throwing. Note that throwing an exception into Rust is undefined behavior +//! anyway, so this should be fine. +//! * We've got some data to transmit across the unwinding boundary, +//! 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 heap allocation because the +//! call stack is preserved while filter functions are being executed. This +//! means that the pointers are passed directly to `_CxxThrowException` which +//! are then recovered in the filter function to be written to the stack frame +//! of the `try` intrinsic. +//! +//! [win64]: http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx +//! [llvm]: http://llvm.org/docs/ExceptionHandling.html#background-on-windows-exceptions + +#![allow(bad_style)] +#![allow(private_no_mangle_fns)] + +use alloc::boxed::Box; +use core::any::Any; +use core::mem; +use core::raw; + +use windows as c; +use libc::{c_int, c_uint}; + +// First up, a whole bunch of type definitions. There's a few platform-specific +// oddities here, and a lot that's just blatantly copied from LLVM. The purpose +// of all this is to implement the `panic` function below through a call to +// `_CxxThrowException`. +// +// This function takes two arguments. The first is a pointer to the data we're +// passing in, which in this case is our trait object. Pretty easy to find! The +// next, however, is more complicated. This is a pointer to a `_ThrowInfo` +// structure, and it generally is just intended to just describe the exception +// being thrown. +// +// Currently the definition of this type [1] is a little hairy, and the main +// oddity (and difference from the online article) is that on 32-bit the +// pointers are pointers but on 64-bit the pointers are expressed as 32-bit +// offsets from the `__ImageBase` symbol. The `ptr_t` and `ptr!` macro in the +// modules below are used to express this. +// +// The maze of type definitions also closely follows what LLVM emits for this +// sort of operation. For example, if you compile this C++ code on MSVC and emit +// the LLVM IR: +// +// #include +// +// void foo() { +// uint64_t a[2] = {0, 1}; +// throw a; +// } +// +// That's essentially what we're trying to emulate. Most of the constant values +// below were just copied from LLVM, I'm at least not 100% sure what's going on +// everywhere. For example the `.PA_K\0` and `.PEA_K\0` strings below (stuck in +// the names of a few of these) I'm not actually sure what they do, but it seems +// to mirror what LLVM does! +// +// In any case, these structures are all constructed in a similar manner, and +// it's just somewhat verbose for us. +// +// [1]: http://www.geoffchappell.com/studies/msvc/language/predefined/ + +#[cfg(target_arch = "x86")] +#[macro_use] +mod imp { + pub type ptr_t = *mut u8; + pub const OFFSET: i32 = 4; + + pub const NAME1: [u8; 7] = [b'.', b'P', b'A', b'_', b'K', 0, 0]; + pub const NAME2: [u8; 7] = [b'.', b'P', b'A', b'X', 0, 0, 0]; + + macro_rules! ptr { + (0) => (0 as *mut u8); + ($e:expr) => ($e as *mut u8); + } +} + +#[cfg(target_arch = "x86_64")] +#[macro_use] +mod imp { + pub type ptr_t = u32; + pub const OFFSET: i32 = 8; + + pub const NAME1: [u8; 7] = [b'.', b'P', b'E', b'A', b'_', b'K', 0]; + pub const NAME2: [u8; 7] = [b'.', b'P', b'E', b'A', b'X', 0, 0]; + + extern { + pub static __ImageBase: u8; + } + + macro_rules! ptr { + (0) => (0); + ($e:expr) => { + (($e as usize) - (&imp::__ImageBase as *const _ as usize)) as u32 + } + } +} + +#[repr(C)] +pub struct _ThrowInfo { + pub attribues: c_uint, + pub pnfnUnwind: imp::ptr_t, + pub pForwardCompat: imp::ptr_t, + pub pCatchableTypeArray: imp::ptr_t, +} + +#[repr(C)] +pub struct _CatchableTypeArray { + pub nCatchableTypes: c_int, + pub arrayOfCatchableTypes: [imp::ptr_t; 2], +} + +#[repr(C)] +pub struct _CatchableType { + pub properties: c_uint, + pub pType: imp::ptr_t, + pub thisDisplacement: _PMD, + pub sizeOrOffset: c_int, + pub copy_function: imp::ptr_t, +} + +#[repr(C)] +pub struct _PMD { + pub mdisp: c_int, + pub pdisp: c_int, + pub vdisp: c_int, +} + +#[repr(C)] +pub struct _TypeDescriptor { + pub pVFTable: *const u8, + pub spare: *mut u8, + pub name: [u8; 7], +} + +static mut THROW_INFO: _ThrowInfo = _ThrowInfo { + attribues: 0, + pnfnUnwind: ptr!(0), + pForwardCompat: ptr!(0), + pCatchableTypeArray: ptr!(0), +}; + +static mut CATCHABLE_TYPE_ARRAY: _CatchableTypeArray = _CatchableTypeArray { + nCatchableTypes: 2, + arrayOfCatchableTypes: [ + ptr!(0), + ptr!(0), + ], +}; + +static mut CATCHABLE_TYPE1: _CatchableType = _CatchableType { + properties: 1, + pType: ptr!(0), + thisDisplacement: _PMD { + mdisp: 0, + pdisp: -1, + vdisp: 0, + }, + sizeOrOffset: imp::OFFSET, + copy_function: ptr!(0), +}; + +static mut CATCHABLE_TYPE2: _CatchableType = _CatchableType { + properties: 1, + pType: ptr!(0), + thisDisplacement: _PMD { + mdisp: 0, + pdisp: -1, + vdisp: 0, + }, + sizeOrOffset: imp::OFFSET, + copy_function: ptr!(0), +}; + +extern { + // The leading `\x01` byte here is actually a magical signal to LLVM to + // *not* apply any other mangling like prefixing with a `_` character. + // + // This symbol is the vtable used by C++'s `std::type_info`. Objects of type + // `std::type_info`, type descriptors, have a pointer to this table. Type + // descriptors are referenced by the C++ EH structures defined above and + // that we construct below. + #[link_name = "\x01??_7type_info@@6B@"] + static TYPE_INFO_VTABLE: *const u8; +} + +// We use #[lang = "msvc_try_filter"] here as this is the type descriptor which +// we'll use in LLVM's `catchpad` instruction which ends up also being passed as +// an argument to the C++ personality function. +// +// Again, I'm not entirely sure what this is describing, it just seems to work. +#[cfg_attr(all(not(test), not(stage0)), + lang = "msvc_try_filter")] +static mut TYPE_DESCRIPTOR1: _TypeDescriptor = _TypeDescriptor { + pVFTable: &TYPE_INFO_VTABLE as *const _ as *const _, + spare: 0 as *mut _, + name: imp::NAME1, +}; + +static mut TYPE_DESCRIPTOR2: _TypeDescriptor = _TypeDescriptor { + pVFTable: &TYPE_INFO_VTABLE as *const _ as *const _, + spare: 0 as *mut _, + name: imp::NAME2, +}; + +pub unsafe fn panic(data: Box) -> u32 { + use core::intrinsics::atomic_store; + + // _CxxThrowException executes entirely on this stack frame, so there's no + // need to otherwise transfer `data` to the heap. We just pass a stack + // pointer to this function. + // + // The first argument is the payload being thrown (our two pointers), and + // the second argument is the type information object describing the + // exception (constructed above). + let ptrs = mem::transmute::<_, raw::TraitObject>(data); + let mut ptrs = [ptrs.data as u64, ptrs.vtable as u64]; + let mut ptrs_ptr = ptrs.as_mut_ptr(); + + // This... may seems surprising, and justifiably so. On 32-bit MSVC the + // pointers between these structure are just that, pointers. On 64-bit MSVC, + // however, the pointers between structures are rather expressed as 32-bit + // offsets from `__ImageBase`. + // + // Consequently, on 32-bit MSVC we can declare all these pointers in the + // `static`s above. On 64-bit MSVC, we would have to express subtraction of + // pointers in statics, which Rust does not currently allow, so we can't + // actually do that. + // + // The next best thing, then is to fill in these structures at runtime + // (panicking is already the "slow path" anyway). So here we reinterpret all + // of these pointer fields as 32-bit integers and then store the + // relevant value into it (atomically, as concurrent panics may be + // happening). Technically the runtime will probably do a nonatomic read of + // these fields, but in theory they never read the *wrong* value so it + // shouldn't be too bad... + // + // In any case, we basically need to do something like this until we can + // express more operations in statics (and we may never be able to). + atomic_store(&mut THROW_INFO.pCatchableTypeArray as *mut _ as *mut u32, + ptr!(&CATCHABLE_TYPE_ARRAY as *const _) as u32); + atomic_store(&mut CATCHABLE_TYPE_ARRAY.arrayOfCatchableTypes[0] as *mut _ as *mut u32, + ptr!(&CATCHABLE_TYPE1 as *const _) as u32); + atomic_store(&mut CATCHABLE_TYPE_ARRAY.arrayOfCatchableTypes[1] as *mut _ as *mut u32, + ptr!(&CATCHABLE_TYPE2 as *const _) as u32); + atomic_store(&mut CATCHABLE_TYPE1.pType as *mut _ as *mut u32, + ptr!(&TYPE_DESCRIPTOR1 as *const _) as u32); + atomic_store(&mut CATCHABLE_TYPE2.pType as *mut _ as *mut u32, + ptr!(&TYPE_DESCRIPTOR2 as *const _) as u32); + + c::_CxxThrowException(&mut ptrs_ptr as *mut _ as *mut _, + &mut THROW_INFO as *mut _ as *mut _); + u32::max_value() +} + +pub fn payload() -> [u64; 2] { + [0; 2] +} + +pub unsafe fn cleanup(payload: [u64; 2]) -> Box { + mem::transmute(raw::TraitObject { + data: payload[0] as *mut _, + vtable: payload[1] as *mut _, + }) +} + +#[lang = "msvc_try_filter"] +#[cfg(stage0)] +unsafe extern fn __rust_try_filter(_eh_ptrs: *mut u8, + _payload: *mut u8) -> i32 { + return 0 +} + +// This is required by the compiler to exist (e.g. it's a lang item), but +// it's never actually called by the compiler because __C_specific_handler +// or _except_handler3 is the personality function that is always used. +// Hence this is just an aborting stub. +#[lang = "eh_personality"] +#[cfg(not(test))] +fn rust_eh_personality() { + unsafe { ::core::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 57281d67ebb4..adb38d857eac 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 000000000000..9cca018ff111 --- /dev/null +++ b/src/libpanic_unwind/windows.rs @@ -0,0 +1,98 @@ +// 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 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)] +pub enum EXCEPTION_DISPOSITION { + ExceptionContinueExecution, + ExceptionContinueSearch, + ExceptionNestedException, + ExceptionCollidedUnwind +} +pub use self::EXCEPTION_DISPOSITION::*; + +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); + #[unwind] + pub fn _CxxThrowException(pExceptionObject: *mut c_void, + pThrowInfo: *mut u8); +} diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index c1a8f747de14..035505655967 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 c2498b7b5eed..fe22cfdb43f7 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 5fe6076e5380..b7dfc8672045 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 82237e0abb76..7d1d5dba3983 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 edb1c4530c24..1bea01c4849e 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 ea4e25754202..2b972af07ff9 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 de0de219db2f..190e8552d199 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 6a79d5df80ae..e4a0731be559 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 d5a9adafe7dc..04b6e1c42b98 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 dd5a643edc1e..72fbbf805153 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 7558e0774b36..4a0c3bbf1870 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 dd453bf99691..cbb4be5b3002 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/librustc_trans/common.rs b/src/librustc_trans/common.rs index 70348cf35e5f..2b7345453d7c 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -463,20 +463,18 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> { // landing pads as "landing pads for SEH". let ccx = self.ccx; let tcx = ccx.tcx(); - let target = &ccx.sess().target.target; match tcx.lang_items.eh_personality() { Some(def_id) if !base::wants_msvc_seh(ccx.sess()) => { Callee::def(ccx, def_id, tcx.mk_substs(Substs::empty())).reify(ccx).val } - _ => if let Some(llpersonality) = ccx.eh_personality().get() { - llpersonality - } else { - let name = if !base::wants_msvc_seh(ccx.sess()) { - "rust_eh_personality" - } else if target.arch == "x86" { - "_except_handler3" + _ => { + if let Some(llpersonality) = ccx.eh_personality().get() { + return llpersonality + } + let name = if base::wants_msvc_seh(ccx.sess()) { + "__CxxFrameHandler3" } else { - "__C_specific_handler" + "rust_eh_personality" }; let fty = Type::variadic_func(&[], &Type::i32(ccx)); let f = declare::declare_cfn(ccx, name, fty); diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index 8653c9773622..e1d5a3f7ee15 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -1096,9 +1096,7 @@ fn trans_msvc_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // We're generating an IR snippet that looks like: // // declare i32 @rust_try(%func, %data, %ptr) { - // %slot = alloca i8* - // call @llvm.localescape(%slot) - // store %ptr, %slot + // %slot = alloca i64* // invoke %func(%data) to label %normal unwind label %catchswitch // // normal: @@ -1108,26 +1106,34 @@ fn trans_msvc_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // %cs = catchswitch within none [%catchpad] unwind to caller // // catchpad: - // %tok = catchpad within %cs [%rust_try_filter] + // %tok = catchpad within %cs [%type_descriptor, 0, %slot] + // %ptr[0] = %slot[0] + // %ptr[1] = %slot[1] // catchret from %tok to label %caught // // caught: // ret i32 1 // } // - // This structure follows the basic usage of the instructions in LLVM - // (see their documentation/test cases for examples), but a - // perhaps-surprising part here is the usage of the `localescape` - // intrinsic. This is used to allow the filter function (also generated - // here) to access variables on the stack of this intrinsic. This - // ability enables us to transfer information about the exception being - // thrown to this point, where we're catching the exception. + // This structure follows the basic usage of throw/try/catch in LLVM. + // For example, compile this C++ snippet to see what LLVM generates: + // + // #include + // + // int bar(void (*foo)(void), uint64_t *ret) { + // try { + // foo(); + // return 0; + // } catch(uint64_t a[2]) { + // ret[0] = a[0]; + // ret[1] = a[1]; + // return 1; + // } + // } // // More information can be found in libstd's seh.rs implementation. - let slot = Alloca(bcx, Type::i8p(ccx), "slot"); - let localescape = ccx.get_intrinsic(&"llvm.localescape"); - Call(bcx, localescape, &[slot], dloc); - Store(bcx, local_ptr, slot); + let i64p = Type::i64(ccx).ptr_to(); + let slot = Alloca(bcx, i64p, "slot"); Invoke(bcx, func, &[data], normal.llbb, catchswitch.llbb, dloc); Ret(normal, C_i32(ccx, 0), dloc); @@ -1135,9 +1141,19 @@ fn trans_msvc_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let cs = CatchSwitch(catchswitch, None, None, 1); AddHandler(catchswitch, cs, catchpad.llbb); - let filter = generate_filter_fn(bcx.fcx, bcx.fcx.llfn); - let filter = BitCast(catchpad, filter, Type::i8p(ccx)); - let tok = CatchPad(catchpad, cs, &[filter]); + let tcx = ccx.tcx(); + let tydesc = match tcx.lang_items.msvc_try_filter() { + Some(did) => ::consts::get_static(ccx, did).to_llref(), + None => bug!("msvc_try_filter not defined"), + }; + let tok = CatchPad(catchpad, cs, &[tydesc, C_i32(ccx, 0), slot]); + let addr = Load(catchpad, slot); + let arg1 = Load(catchpad, addr); + let val1 = C_i32(ccx, 1); + let arg2 = Load(catchpad, InBoundsGEP(catchpad, addr, &[val1])); + let local_ptr = BitCast(catchpad, local_ptr, i64p); + Store(catchpad, arg1, local_ptr); + Store(catchpad, arg2, InBoundsGEP(catchpad, local_ptr, &[val1])); CatchRet(catchpad, tok, caught.llbb); Ret(caught, C_i32(ccx, 1), dloc); @@ -1289,89 +1305,6 @@ fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, return rust_try } -// For MSVC-style exceptions (SEH), the compiler generates a filter function -// which is used to determine whether an exception is being caught (e.g. if it's -// a Rust exception or some other). -// -// This function is used to generate said filter function. The shim generated -// here is actually just a thin wrapper to call the real implementation in the -// standard library itself. For reasons as to why, see seh.rs in the standard -// library. -fn generate_filter_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, - rust_try_fn: ValueRef) - -> ValueRef { - let ccx = fcx.ccx; - let tcx = ccx.tcx(); - let dloc = DebugLoc::None; - - let rust_try_filter = match tcx.lang_items.msvc_try_filter() { - Some(did) => { - Callee::def(ccx, did, tcx.mk_substs(Substs::empty())).reify(ccx).val - } - None => bug!("msvc_try_filter not defined"), - }; - - let output = ty::FnOutput::FnConverging(tcx.types.i32); - let i8p = tcx.mk_mut_ptr(tcx.types.i8); - - let frameaddress = ccx.get_intrinsic(&"llvm.frameaddress"); - let recoverfp = ccx.get_intrinsic(&"llvm.x86.seh.recoverfp"); - let localrecover = ccx.get_intrinsic(&"llvm.localrecover"); - - // On all platforms, once we have the EXCEPTION_POINTERS handle as well as - // the base pointer, we follow the standard layout of: - // - // block: - // %parentfp = call i8* llvm.x86.seh.recoverfp(@rust_try_fn, %bp) - // %arg = call i8* llvm.localrecover(@rust_try_fn, %parentfp, 0) - // %ret = call i32 @the_real_filter_function(%ehptrs, %arg) - // ret i32 %ret - // - // The recoverfp intrinsic is used to recover the frame pointer of the - // `rust_try_fn` function, which is then in turn passed to the - // `localrecover` intrinsic (pairing with the `localescape` intrinsic - // mentioned above). Putting all this together means that we now have a - // handle to the arguments passed into the `try` function, allowing writing - // to the stack over there. - // - // For more info, see seh.rs in the standard library. - let do_trans = |bcx: Block, ehptrs, base_pointer| { - let rust_try_fn = BitCast(bcx, rust_try_fn, Type::i8p(ccx)); - let parentfp = Call(bcx, recoverfp, &[rust_try_fn, base_pointer], dloc); - let arg = Call(bcx, localrecover, - &[rust_try_fn, parentfp, C_i32(ccx, 0)], dloc); - let ret = Call(bcx, rust_try_filter, &[ehptrs, arg], dloc); - Ret(bcx, ret, dloc); - }; - - if ccx.tcx().sess.target.target.arch == "x86" { - // On x86 the filter function doesn't actually receive any arguments. - // Instead the %ebp register contains some contextual information. - // - // Unfortunately I don't know of any great documentation as to what's - // going on here, all I can say is that there's a few tests cases in - // LLVM's test suite which follow this pattern of instructions, so we - // just do the same. - gen_fn(fcx, "__rustc_try_filter", vec![], output, &mut |bcx| { - let ebp = Call(bcx, frameaddress, &[C_i32(ccx, 1)], dloc); - let exn = InBoundsGEP(bcx, ebp, &[C_i32(ccx, -20)]); - let exn = Load(bcx, BitCast(bcx, exn, Type::i8p(ccx).ptr_to())); - do_trans(bcx, exn, ebp); - }) - } else if ccx.tcx().sess.target.target.arch == "x86_64" { - // Conveniently on x86_64 the EXCEPTION_POINTERS handle and base pointer - // are passed in as arguments to the filter function, so we just pass - // those along. - gen_fn(fcx, "__rustc_try_filter", vec![i8p, i8p], output, &mut |bcx| { - let exn = llvm::get_param(bcx.fcx.llfn, 0); - let rbp = llvm::get_param(bcx.fcx.llfn, 1); - do_trans(bcx, exn, rbp); - }) - } else { - bug!("unknown target to generate a filter function") - } -} - fn span_invalid_monomorphization_error(a: &Session, b: Span, c: &str) { span_err!(a, b, E0511, "{}", c); } diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index 29bd28b6160c..6d33fb311cff 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 c32bca82bd5a..e879d440643f 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 d4b40b844fce..8f41bdf39e97 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 39adda1066a1..d69789cedaf2 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 bbd89af01a73..9a195d93afde 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 fd6a15b0f69a..b85d4b330a6e 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,34 @@ 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! +#[allow(improper_ctypes)] +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 +89,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 +114,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 +134,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 +144,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 +154,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 +169,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 +227,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 +371,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 +380,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 83091c72c0d6..6eee4ee9bbe5 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 56628a4c7545..c9279883ae59 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 527c2e63030d..000000000000 --- 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/common/unwind/seh.rs b/src/libstd/sys/common/unwind/seh.rs deleted file mode 100644 index 94da42f0092f..000000000000 --- a/src/libstd/sys/common/unwind/seh.rs +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Windows SEH -//! -//! On Windows (currently only on MSVC), the default exception handling -//! mechanism is Structured Exception Handling (SEH). This is quite different -//! than Dwarf-based exception handling (e.g. what other unix platforms use) in -//! terms of compiler internals, so LLVM is required to have a good deal of -//! extra support for SEH. -//! -//! In a nutshell, what happens here is: -//! -//! 1. The `panic` function calls the standard Windows function `RaiseException` -//! with a Rust-specific code, triggering the unwinding process. -//! 2. All landing pads generated by the compiler use the personality function -//! `__C_specific_handler` on 64-bit and `__except_handler3` on 32-bit, -//! functions in the CRT, and the unwinding code in Windows will use this -//! personality function to execute all cleanup code on the stack. -//! 3. All compiler-generated calls to `invoke` have a landing pad set as a -//! `cleanuppad` LLVM instruction, which indicates the start of the cleanup -//! routine. The personality (in step 2, defined in the CRT) is responsible -//! for running the cleanup routines. -//! 4. Eventually the "catch" code in the `try` intrinsic (generated by the -//! compiler) is executed, which will ensure that the exception being caught -//! is indeed a Rust exception, indicating that control should come back to -//! Rust. This is done via a `catchswitch` plus a `catchpad` instruction in -//! LLVM IR terms, finally returning normal control to the program with a -//! `catchret` instruction. The `try` intrinsic uses a filter function to -//! detect what kind of exception is being thrown, and this detection is -//! implemented as the msvc_try_filter language item below. -//! -//! Some specific differences from the gcc-based exception handling are: -//! -//! * Rust has no custom personality function, it is instead *always* -//! __C_specific_handler or __except_handler3, so the filtering is done in a -//! C++-like manner instead of in the personality function itself. Note that -//! 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 -//! 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 -//! that the pointers are passed directly to `RaiseException` which are then -//! recovered in the filter function to be written to the stack frame of the -//! `try` intrinsic. -//! -//! [win64]: http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx -//! [llvm]: http://llvm.org/docs/ExceptionHandling.html#background-on-windows-exceptions - -use sys::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 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 _, - }) - } - - // 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 -// it's never actually called by the compiler because __C_specific_handler -// or _except_handler3 is the personality function that is always used. -// Hence this is just an aborting stub. -#[lang = "eh_personality"] -#[cfg(not(test))] -fn rust_eh_personality() { - unsafe { ::intrinsics::abort() } -} diff --git a/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs b/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs index 8d8809171667..5f7ad6918cdd 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 ab24b9e6fd60..2acf6485eb3d 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 2f0dec759b3d..99e6ba8c770b 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 67bce440d4d4..7958162986cf 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 cf797b85f548..acef98f2afc1 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)), @@ -438,6 +440,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 c6a70edf2a8f..88be3ade8392 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 000000000000..dca222c49d0a --- /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 000000000000..ebe6fd54799e --- /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 000000000000..3ff61fd93d75 --- /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 c1e9782852a7..ce74e5de3d46 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/llvm b/src/llvm index 751345228a0e..a73c41e7f1c8 160000 --- a/src/llvm +++ b/src/llvm @@ -1 +1 @@ -Subproject commit 751345228a0ef03fd147394bb5104359b7a808be +Subproject commit a73c41e7f1c85cd814e9792fc6a6a8f8e31b8dd4 diff --git a/src/rustc/std_shim/Cargo.lock b/src/rustc/std_shim/Cargo.lock index 530c04da8a0c..bad46966ffa6 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/rustllvm/llvm-auto-clean-trigger b/src/rustllvm/llvm-auto-clean-trigger index eeb16d7ac5fc..4017c3856c46 100644 --- a/src/rustllvm/llvm-auto-clean-trigger +++ b/src/rustllvm/llvm-auto-clean-trigger @@ -1,4 +1,4 @@ # If this file is modified, then llvm will be forcibly cleaned and then rebuilt. # The actual contents of this file do not matter, but to trigger a change on the # build bots then the contents should be changed so git updates the mtime. -2016-04-26 +2016-04-28 diff --git a/src/test/codegen/lto-removes-invokes.rs b/src/test/codegen/lto-removes-invokes.rs new file mode 100644 index 000000000000..b2f43489523d --- /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 000000000000..c3242a5082b6 --- /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 000000000000..d6c21fecf6b5 --- /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 000000000000..3b74156b6b01 --- /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 000000000000..fbf70b3d3fef --- /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 000000000000..4bb36839d988 --- /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 000000000000..4bb36839d988 --- /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 000000000000..b90dec9281bc --- /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 000000000000..e1902e44a60e --- /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 000000000000..2183338b2498 --- /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 000000000000..f067b6b8349b --- /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 000000000000..0ecf65f080fa --- /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 000000000000..5f6f4ecbd6f6 --- /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 000000000000..02f3da58f1da --- /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 000000000000..0681f991067b --- /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 000000000000..885b3e6dbb94 --- /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 000000000000..0fe0da2fa2c5 --- /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 000000000000..b178006411ba --- /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 000000000000..de8e010c3cb4 --- /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 000000000000..88ad36f310eb --- /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 000000000000..c42a25a553a7 --- /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 000000000000..71c1a61062d1 --- /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 000000000000..2fc9d6cfd04a --- /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 000000000000..9e5fc592b1a3 --- /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 000000000000..71e35e41fc04 --- /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 000000000000..dec8f738d329 --- /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 000000000000..09e33b88189f --- /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 000000000000..10e633b3775b --- /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 77dcf9c1bd81..6a9d52cb0048 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 \