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).
This commit is contained in:
Alex Crichton 2016-04-08 16:18:40 -07:00
parent 32683ce193
commit 0ec321f7b5
76 changed files with 2000 additions and 745 deletions

View File

@ -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

View File

@ -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

View File

@ -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::<Vec<_>>());
}
// 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()) {

View File

@ -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 {

View File

@ -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" }

112
src/libpanic_abort/lib.rs Normal file
View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Implementation of Rust 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() {}

27
src/libpanic_unwind/Cargo.lock generated Normal file
View File

@ -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",
]

View File

@ -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" }

View File

@ -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;

View File

@ -18,7 +18,6 @@
pub mod eh;
use prelude::v1::*;
use core::mem;
pub struct DwarfReader {

View File

@ -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<Box<Any + Send + 'static>>,
_uwe: uw::_Unwind_Exception,
cause: Option<Box<Any + Send>>,
}
pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
let exception: Box<_> = box Exception {
uwe: uw::_Unwind_Exception {
pub unsafe fn panic(data: Box<Any + Send>) -> 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<Any + Send + 'static> {
pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send> {
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);

109
src/libpanic_unwind/lib.rs Normal file
View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Implementation of 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 (),
}))
}

View File

@ -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<Any + Send + 'static>`. Like with Dwarf exceptions
//! specifically a `Box<Any + Send>`. 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<Any + Send + 'static>) -> ! {
// As mentioned above, the call stack here is preserved while the filter
// functions are running, so it's ok to pass stack-local arrays into
// `RaiseException`.
//
// The two pointers of the `data` trait object are written to the stack,
// passed to `RaiseException`, and they're later extracted by the filter
// function below in the "custom exception information" section of the
// `EXCEPTION_RECORD` type.
let ptrs = mem::transmute::<_, raw::TraitObject>(data);
let ptrs = [ptrs.data, ptrs.vtable];
c::RaiseException(RUST_PANIC, 0, 2, ptrs.as_ptr() as *mut _);
rtabort!("could not unwind stack");
}
pub fn payload() -> [usize; 2] {
[0; 2]
}
pub unsafe fn cleanup(payload: [usize; 2]) -> Box<Any + Send + 'static> {
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:
pub unsafe fn panic(data: Box<Any + Send>) -> 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`.
//
// * 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<Any>`, 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
// 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 unsafe fn cleanup(payload: [usize; 2]) -> Box<Any + Send> {
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<Any>`, 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() }
}

View File

@ -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<Any + Send + 'static>
data: Box<Any + Send>
}
pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
pub unsafe fn panic(data: Box<Any + Send>) -> 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,
&params 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<Any + Send + 'static> {
pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send> {
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,
&params as *const c::ULONG_PTR);
rtabort!("could not resume unwind");
intrinsics::abort();
}
unsafe fn find_landing_pad(dc: &c::DISPATCHER_CONTEXT) -> Option<usize> {

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(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);
}

View File

@ -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<ExternCrate>;
fn crate_attrs(&self, cnum: ast::CrateNum) -> Vec<ast::Attribute>;
/// 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<ExternCrate> { bug!("extern_crate") }
fn crate_attrs(&self, cnum: ast::CrateNum) -> Vec<ast::Attribute>
{ bug!("crate_attrs") }

View File

@ -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<DependencyList> {
}
}).collect::<Vec<_>>();
// 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<ast::CrateNum>,
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);
}
}

View File

@ -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)));

View File

@ -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<usize> = (None, parse_opt_uint,
"set the inlining threshold for"),
panic: PanicStrategy = (PanicStrategy::Unwind, parse_panic_strategy,
"panic strategy to compile crate with"),
}

View File

@ -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<usize>,
/// 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<Option<ast::CrateNum>>,
pub injected_panic_runtime: Cell<Option<ast::CrateNum>>,
/// 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()),
};

View File

@ -250,3 +250,5 @@ pub fn rustc_version() -> String {
option_env!("CFG_VERSION").unwrap_or("unknown version")
)
}
pub const tag_panic_strategy: usize = 0x114;

View File

@ -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) {
// 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);
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
}
// 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.
// Before we inject any dependencies, make sure we don't inject a
// 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);

View File

@ -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<ast::Attribute>
{
decoder::get_crate_attributes(self.get_crate_data(cnum).data())

View File

@ -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 {

View File

@ -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),
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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" }

View File

@ -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");

View File

@ -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;

View File

@ -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<Any>` 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<Any>` 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

View File

@ -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, F: FnOnce() -> R> FnOnce<()> for AssertRecoverSafe<F> {
/// ```
#[stable(feature = "catch_unwind", since = "1.9.0")]
pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
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<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
/// ```
#[stable(feature = "resume_unwind", since = "1.9.0")]
pub fn resume_unwind(payload: Box<Any + Send>) -> ! {
unwind::rust_panic(payload)
panicking::rust_panic(payload)
}
/// Deprecated, use resume_unwind instead

View File

@ -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<usize> = Cell::new(0) }
thread_local! {
pub static LOCAL_STDERR: RefCell<Option<Box<Write + Send>>> = {
RefCell::new(None)
}
}
thread_local! { pub static PANIC_COUNT: Cell<usize> = 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<Fn(&PanicInfo) + 'static + Sync + Send>) {
if thread::panicking() {
panic!("cannot modify the panic hook from a panicking thread");
@ -82,7 +113,7 @@ pub fn set_hook(hook: Box<Fn(&PanicInfo) + 'static + Sync + Send>) {
/// # 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<Fn(&PanicInfo) + 'static + Sync + Send> {
if thread::panicking() {
panic!("cannot modify the panic hook from a panicking thread");
@ -102,7 +133,7 @@ pub fn take_hook() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> {
}
/// 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: FnOnce() -> R>(f: F) -> Result<R, Box<Any + Send>> {
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::<fn(&mut _), fn(*mut u8)>(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 _,
}))
}
});
// 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 {
return ret.map(|()| {
slot.take().unwrap()
});
fn get_call<F: FnMut()>(_: &mut F) -> fn(&mut F) {
call
}
fn call<F: FnMut()>(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<M: Any + Send>(msg: M, file_line: &(&'static str, u32)) -> ! {
// Note that this should be the only allocation performed in this code path.
// Currently this means that panic!() on OOM will invoke this code path,
// but then again we're not really ready for panic on OOM anyway. If
// we do start doing this, then we should propagate this allocation to
// be performed in the parent of this thread instead of the thread that's
// panicking.
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<Any>` 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<Any + Send>,
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<Any + Send>) -> ! {
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)
}

View File

@ -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"]

View File

@ -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;

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Implementation of Rust stack unwinding
//!
//! For background on exception handling and stack unwinding please see
//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
//! documents linked from it.
//! These are also good reads:
//! http://mentorembedded.github.io/cxx-abi/abi-eh.html
//! http://monoinfinito.wordpress.com/series/exception-handling-in-c/
//! http://www.airs.com/blog/index.php?s=exception+frames
//!
//! ## A brief summary
//!
//! Exception handling happens in two phases: a search phase and a cleanup phase.
//!
//! In both phases the unwinder walks stack frames from top to bottom using
//! information from the stack frame unwind sections of the current process's
//! modules ("module" here refers to an OS module, i.e. an executable or a
//! dynamic library).
//!
//! For each stack frame, it invokes the associated "personality routine", whose
//! address is also stored in the unwind info section.
//!
//! In the search phase, the job of a personality routine is to examine exception
//! object being thrown, and to decide whether it should be caught at that stack
//! frame. Once the handler frame has been identified, cleanup phase begins.
//!
//! In the cleanup phase, 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: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> {
let mut f = Some(f);
return inner_try(try_fn::<F>, &mut f as *mut _ as *mut u8);
fn try_fn<F: FnOnce()>(opt_closure: *mut u8) {
let opt_closure = opt_closure as *mut Option<F>;
unsafe { (*opt_closure).take().unwrap()(); }
}
}
unsafe fn inner_try(f: fn(*mut u8), data: *mut u8)
-> Result<(), Box<Any + Send>> {
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<Any + Send + 'static>) -> ! {
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<M: Any + Send>(msg: M, file_line: &(&'static str, u32)) -> ! {
// Note that this should be the only allocation performed in this code path.
// Currently this means that panic!() on OOM will invoke this code path,
// but then again we're not really ready for panic on OOM anyway. If
// we do start doing this, then we should propagate this allocation to
// be performed in the parent of this thread instead of the thread that's
// panicking.
// see below for why we do the `Any` coercion here.
begin_unwind_inner(Box::new(msg), file_line)
}
/// The core of the unwinding.
///
/// This is non-generic to avoid instantiation bloat in other crates
/// (which makes compilation of small crates noticeably slower). (Note:
/// we need the `Any` object anyway, we're not just creating it to
/// avoid being generic.)
///
/// Doing this split took the LLVM IR line counts of `fn main() { panic!()
/// }` from ~1900/3700 (-O/no opts) to 180/590.
#[inline(never)] #[cold] // this is the slow path, please never inline this
fn begin_unwind_inner(msg: Box<Any + Send>,
file_line: &(&'static str, u32)) -> ! {
let (file, line) = *file_line;
// First, invoke the default panic handler.
panicking::on_panic(&*msg, file, line);
// Finally, perform the unwinding.
rust_panic(msg);
}

View File

@ -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
}
}

View File

@ -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,

View File

@ -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.

View File

@ -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))

View File

@ -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 \

View File

@ -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::*;

13
src/libunwind/Cargo.toml Normal file
View File

@ -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" }

39
src/libunwind/build.rs Normal file
View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use 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");
}
}

30
src/libunwind/lib.rs Normal file
View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![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::*;

View File

@ -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
}

View File

@ -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,12 +49,8 @@ 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"
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)",
]
[[package]]
name = "libc"
@ -72,6 +59,24 @@ dependencies = [
"core 0.0.0",
]
[[package]]
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]]
name = "rand"
version = "0.0.0"
@ -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",
]

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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!");
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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() {
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// no-prefer-dynamic
#![feature(needs_panic_runtime)]
#![crate_type = "rlib"]
#![needs_panic_runtime]
#![no_std]

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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() {}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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() {}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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() {}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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() {}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// no-prefer-dynamic
#![feature(panic_runtime)]
#![crate_type = "rlib"]
#![panic_runtime]
#![no_std]
extern crate needs_panic_runtime;

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags:-C panic=abort
// no-prefer-dynamic
#![crate_type = "rlib"]
#![no_std]
extern crate panic_runtime_abort;

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// no-prefer-dynamic
#![crate_type = "rlib"]
#![no_std]
extern crate panic_runtime_unwind;

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags:-C panic=foo
// error-pattern:either `panic` or `abort` was expected
fn main() {}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags:-C panic
// error-pattern:requires either `panic` or `abort`
fn main() {}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern:is not compiled with this crate's panic strategy `abort`
// compile-flags:-C panic=abort
#![feature(test)]
extern crate test;
fn main() {
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![panic_runtime] //~ ERROR: is an experimental feature
#![needs_panic_runtime] //~ ERROR: is an experimental feature
fn main() {}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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;

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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() {}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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() {}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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() {}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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() {}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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() {}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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() {}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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");
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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));
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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();
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags:-C panic=abort
// no-prefer-dynamic
#![feature(panic_abort)]
extern crate panic_abort;
fn main() {
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// no-prefer-dynamic
#![feature(panic_unwind)]
extern crate panic_unwind;
fn main() {
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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));
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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"));
}

View File

@ -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 \