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