Browse Source

Merge pull request #44 from FenrirWolf/panic-unwind

Add unwinding strategy for panics
pull/10/head
Ronald Kinard 7 years ago committed by GitHub
parent
commit
e5d15c3139
  1. 2
      .travis.yml
  2. 6
      ctr-std/src/collections/hash/table.rs
  3. 21
      ctr-std/src/lib.rs
  4. 11
      ctr-std/src/panic.rs
  5. 613
      ctr-std/src/panicking.rs
  6. 18
      ctr-std/src/rt.rs
  7. 4
      ctr-std/src/sys_common/mod.rs
  8. 3
      examples/.cargo/config
  9. 1
      examples/3ds.json
  10. 4
      examples/Cargo.toml
  11. 2
      examples/Xargo.toml

2
.travis.yml

@ -1,7 +1,7 @@
language: rust language: rust
rust: rust:
- nightly-2017-07-18 - nightly-2017-07-31
- nightly - nightly
matrix: matrix:

6
ctr-std/src/collections/hash/table.rs

@ -44,7 +44,7 @@ impl TaggedHashUintPtr {
#[inline] #[inline]
unsafe fn new(ptr: *mut HashUint) -> Self { unsafe fn new(ptr: *mut HashUint) -> Self {
debug_assert!(ptr as usize & 1 == 0 || ptr as usize == EMPTY as usize); debug_assert!(ptr as usize & 1 == 0 || ptr as usize == EMPTY as usize);
TaggedHashUintPtr(Unique::new(ptr)) TaggedHashUintPtr(Unique::new_unchecked(ptr))
} }
#[inline] #[inline]
@ -56,7 +56,7 @@ impl TaggedHashUintPtr {
} else { } else {
usize_ptr &= !1; usize_ptr &= !1;
} }
self.0 = Unique::new(usize_ptr as *mut HashUint) self.0 = Unique::new_unchecked(usize_ptr as *mut HashUint)
} }
} }
@ -877,7 +877,7 @@ impl<K, V> RawTable<K, V> {
elems_left: elems_left, elems_left: elems_left,
marker: marker::PhantomData, marker: marker::PhantomData,
}, },
table: unsafe { Shared::new(self) }, table: Shared::from(self),
marker: marker::PhantomData, marker: marker::PhantomData,
} }
} }

21
ctr-std/src/lib.rs

@ -26,6 +26,7 @@
#![feature(lang_items)] #![feature(lang_items)]
#![feature(macro_reexport)] #![feature(macro_reexport)]
#![feature(needs_drop)] #![feature(needs_drop)]
#![feature(needs_panic_runtime)]
#![feature(oom)] #![feature(oom)]
#![feature(on_unimplemented)] #![feature(on_unimplemented)]
#![feature(optin_builtin_traits)] #![feature(optin_builtin_traits)]
@ -45,10 +46,13 @@
#![feature(unicode)] #![feature(unicode)]
#![feature(unique)] #![feature(unique)]
#![feature(untagged_unions)] #![feature(untagged_unions)]
#![feature(unwind_attributes)]
#![feature(zero_one)] #![feature(zero_one)]
#![allow(non_camel_case_types, dead_code, unused_features)] #![allow(non_camel_case_types, dead_code, unused_features)]
#![no_std] #![no_std]
#![needs_panic_runtime]
#![cfg_attr(not(stage0), default_lib_allocator)] #![cfg_attr(not(stage0), default_lib_allocator)]
#![stable(feature = "rust1", since = "1.0.0")] #![stable(feature = "rust1", since = "1.0.0")]
@ -197,20 +201,3 @@ pub mod rt;
pub mod __rand { pub mod __rand {
pub use rand::{thread_rng, ThreadRng, Rng}; pub use rand::{thread_rng, ThreadRng, Rng};
} }
// NOTE: These two are "undefined" symbols that LLVM emits but that
// we never actually use
#[doc(hidden)]
#[stable(feature = "3ds", since = "1.0.0")]
#[no_mangle]
pub unsafe extern "C" fn __aeabi_unwind_cpp_pr0() {
intrinsics::unreachable()
}
#[stable(feature = "3ds", since = "1.0.0")]
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn __aeabi_unwind_cpp_pr1() {
intrinsics::unreachable()
}

11
ctr-std/src/panic.rs

@ -22,8 +22,8 @@ use rc::Rc;
use sync::{Arc, Mutex, RwLock, atomic}; use sync::{Arc, Mutex, RwLock, atomic};
use thread::Result; use thread::Result;
//#[stable(feature = "panic_hooks", since = "1.10.0")] #[stable(feature = "panic_hooks", since = "1.10.0")]
//pub use panicking::{take_hook, set_hook, PanicInfo, Location}; pub use panicking::{take_hook, set_hook, PanicInfo, Location};
/// A marker trait which represents "panic safe" types in Rust. /// A marker trait which represents "panic safe" types in Rust.
/// ///
@ -112,7 +112,7 @@ pub trait UnwindSafe {}
/// This is a "helper marker trait" used to provide impl blocks for the /// This is a "helper marker trait" used to provide impl blocks for the
/// `UnwindSafe` trait, for more information see that documentation. /// `UnwindSafe` trait, for more information see that documentation.
#[stable(feature = "catch_unwind", since = "1.9.0")] #[stable(feature = "catch_unwind", since = "1.9.0")]
#[rustc_on_unimplemented = "the type {Self} contains interior mutability \ #[rustc_on_unimplemented = "the type {Self} may contain interior mutability \
and a reference may not be safely transferrable \ and a reference may not be safely transferrable \
across a catch_unwind boundary"] across a catch_unwind boundary"]
pub trait RefUnwindSafe {} pub trait RefUnwindSafe {}
@ -388,7 +388,6 @@ pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
/// } /// }
/// ``` /// ```
#[stable(feature = "resume_unwind", since = "1.9.0")] #[stable(feature = "resume_unwind", since = "1.9.0")]
// we always abort so I'm pretty sure there's no reason to ever call this pub fn resume_unwind(payload: Box<Any + Send>) -> ! {
pub fn resume_unwind(_payload: Box<Any + Send>) -> ! { panicking::update_count_then_panic(payload)
unimplemented!()
} }

613
ctr-std/src/panicking.rs

@ -10,16 +10,26 @@
//! Implementation of various bits and pieces of the `panic!` macro and //! Implementation of various bits and pieces of the `panic!` macro and
//! associated runtime pieces. //! 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 io::prelude::*; use io::prelude::*;
use any::Any; use any::Any;
use cell::RefCell; use cell::RefCell;
use fmt::{self, Display}; use fmt;
use intrinsics;
use mem; use mem;
use ptr; use ptr;
use raw; use raw;
use sys_common::rwlock::RWLock;
use sys_common::thread_info; use sys_common::thread_info;
use sys_common::util;
use thread;
thread_local! { thread_local! {
pub static LOCAL_STDERR: RefCell<Option<Box<Write + Send>>> = { pub static LOCAL_STDERR: RefCell<Option<Box<Write + Send>>> = {
@ -27,105 +37,414 @@ thread_local! {
} }
} }
///The compiler wants this to be here. Otherwise it won't be happy. And we like happy compilers. #[allow(improper_ctypes)]
#[lang = "eh_personality"] extern {
pub extern fn eh_personality() {} 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;
}
/// Determines whether the current thread is unwinding because of panic. #[derive(Copy, Clone)]
// Which it won't be, since we still don't have stack unwinding. enum Hook {
pub fn panicking() -> bool { Default,
false Custom(*mut (Fn(&PanicInfo) + 'static + Sync + Send)),
} }
/// Entry point of panic from the libcore crate. static HOOK_LOCK: RWLock = RWLock::new();
#[lang = "panic_fmt"] static mut HOOK: Hook = Hook::Default;
pub extern fn rust_begin_panic(msg: fmt::Arguments,
file: &'static str, /// Registers a custom panic hook, replacing any that was previously registered.
line: u32, ///
col: u32) -> ! { /// The panic hook is invoked when a thread panics, but before the panic runtime
begin_panic_fmt(&msg, &(file, line, col)) /// is invoked. As such, the hook will run with both the aborting and unwinding
/// runtimes. The default hook prints a message to standard error and generates
/// a backtrace if requested, but this behavior can be customized with the
/// `set_hook` and `take_hook` functions.
///
/// The hook is provided with a `PanicInfo` struct which contains information
/// about the origin of the panic, including the payload passed to `panic!` and
/// the source code location from which the panic originated.
///
/// The panic hook is a global resource.
///
/// # Panics
///
/// Panics if called from a panicking thread.
///
/// # Examples
///
/// The following will print "Custom panic hook":
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|_| {
/// println!("Custom panic hook");
/// }));
///
/// panic!("Normal panic");
/// ```
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub fn set_hook(hook: Box<Fn(&PanicInfo) + 'static + Sync + Send>) {
if thread::panicking() {
panic!("cannot modify the panic hook from a panicking thread");
}
unsafe {
HOOK_LOCK.write();
let old_hook = HOOK;
HOOK = Hook::Custom(Box::into_raw(hook));
HOOK_LOCK.write_unlock();
if let Hook::Custom(ptr) = old_hook {
Box::from_raw(ptr);
}
}
} }
/// The entry point for panicking with a formatted message. /// Unregisters the current panic hook, returning it.
/// ///
/// This is designed to reduce the amount of code required at the call /// If no custom hook is registered, the default hook will be returned.
/// 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 /// # Panics
/// the actual formatting into this shared place. ///
#[unstable(feature = "libstd_sys_internals", /// Panics if called from a panicking thread.
reason = "used by the panic! macro", ///
issue = "0")] /// # Examples
#[inline(never)] #[cold] ///
pub fn begin_panic_fmt(msg: &fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! { /// The following will print "Normal panic":
use fmt::Write; ///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|_| {
/// println!("Custom panic hook");
/// }));
///
/// let _ = panic::take_hook();
///
/// panic!("Normal panic");
/// ```
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub fn take_hook() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> {
if thread::panicking() {
panic!("cannot modify the panic hook from a panicking thread");
}
let mut s = String::new(); unsafe {
let _ = s.write_fmt(*msg); HOOK_LOCK.write();
begin_panic(s, file_line_col); let hook = HOOK;
HOOK = Hook::Default;
HOOK_LOCK.write_unlock();
match hook {
Hook::Default => Box::new(default_hook),
Hook::Custom(ptr) => Box::from_raw(ptr),
}
}
}
/// A struct providing information about a panic.
///
/// `PanicInfo` structure is passed to a panic hook set by the [`set_hook`]
/// function.
///
/// [`set_hook`]: ../../std/panic/fn.set_hook.html
///
/// # Examples
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|panic_info| {
/// println!("panic occured: {:?}", panic_info.payload().downcast_ref::<&str>().unwrap());
/// }));
///
/// panic!("Normal panic");
/// ```
#[stable(feature = "panic_hooks", since = "1.10.0")]
#[derive(Debug)]
pub struct PanicInfo<'a> {
payload: &'a (Any + Send),
location: Location<'a>,
}
impl<'a> PanicInfo<'a> {
/// Returns the payload associated with the panic.
///
/// This will commonly, but not always, be a `&'static str` or [`String`].
///
/// [`String`]: ../../std/string/struct.String.html
///
/// # Examples
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|panic_info| {
/// println!("panic occured: {:?}", panic_info.payload().downcast_ref::<&str>().unwrap());
/// }));
///
/// panic!("Normal panic");
/// ```
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub fn payload(&self) -> &(Any + Send) {
self.payload
}
/// Returns information about the location from which the panic originated,
/// if available.
///
/// This method will currently always return [`Some`], but this may change
/// in future versions.
///
/// [`Some`]: ../../std/option/enum.Option.html#variant.Some
///
/// # Examples
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|panic_info| {
/// if let Some(location) = panic_info.location() {
/// println!("panic occured in file '{}' at line {}", location.file(), location.line());
/// } else {
/// println!("panic occured but can't get location information...");
/// }
/// }));
///
/// panic!("Normal panic");
/// ```
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub fn location(&self) -> Option<&Location> {
Some(&self.location)
}
}
/// A struct containing information about the location of a panic.
///
/// This structure is created by the [`location`] method of [`PanicInfo`].
///
/// [`location`]: ../../std/panic/struct.PanicInfo.html#method.location
/// [`PanicInfo`]: ../../std/panic/struct.PanicInfo.html
///
/// # Examples
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|panic_info| {
/// if let Some(location) = panic_info.location() {
/// println!("panic occured in file '{}' at line {}", location.file(), location.line());
/// } else {
/// println!("panic occured but can't get location information...");
/// }
/// }));
///
/// panic!("Normal panic");
/// ```
#[derive(Debug)]
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub struct Location<'a> {
file: &'a str,
line: u32,
col: u32,
}
impl<'a> Location<'a> {
/// Returns the name of the source file from which the panic originated.
///
/// # Examples
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|panic_info| {
/// if let Some(location) = panic_info.location() {
/// println!("panic occured in file '{}'", location.file());
/// } else {
/// println!("panic occured but can't get location information...");
/// }
/// }));
///
/// panic!("Normal panic");
/// ```
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub fn file(&self) -> &str {
self.file
}
/// Returns the line number from which the panic originated.
///
/// # Examples
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|panic_info| {
/// if let Some(location) = panic_info.location() {
/// println!("panic occured at line {}", location.line());
/// } else {
/// println!("panic occured but can't get location information...");
/// }
/// }));
///
/// panic!("Normal panic");
/// ```
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub fn line(&self) -> u32 {
self.line
}
/// Returns the column from which the panic originated.
///
/// # Examples
///
/// ```should_panic
/// #![feature(panic_col)]
/// use std::panic;
///
/// panic::set_hook(Box::new(|panic_info| {
/// if let Some(location) = panic_info.location() {
/// println!("panic occured at column {}", location.column());
/// } else {
/// println!("panic occured but can't get location information...");
/// }
/// }));
///
/// panic!("Normal panic");
/// ```
#[unstable(feature = "panic_col", reason = "recently added", issue = "42939")]
pub fn column(&self) -> u32 {
self.col
}
} }
// Citra doesn't support the Error applet yet
#[cfg(feature = "citra")] #[cfg(feature = "citra")]
#[unstable(feature = "libstd_sys_internals", fn default_hook(info: &PanicInfo) {
reason = "used by the panic! macro", #[cfg(feature = "backtrace")]
issue = "0")] use sys_common::backtrace;
#[inline(never)] #[cold]
pub fn begin_panic<M: Any + Send + Display>(msg: M, file_line_col: &(&'static str, u32, u32)) -> ! {
let msg = Box::new(msg);
let (file, line, col) = *file_line_col;
// 3DS-specific code begins here // If this is a double panic, make sure that we print a backtrace
use libctru::{consoleInit, consoleClear, gfxScreen_t, threadGetCurrent}; // for this panic. Otherwise only print it if logging is enabled.
#[cfg(feature = "backtrace")]
let log_backtrace = {
let panics = update_panic_count(0);
unsafe { if panics >= 2 {
// Set up a new console, overwriting whatever was on the top screen Some(backtrace::PrintFormat::Full)
// before we started panicking } else {
let _console = consoleInit(gfxScreen_t::GFX_TOP, ptr::null_mut()); backtrace::log_enabled()
consoleClear(); }
};
// Determine thread name let file = info.location.file;
let line = info.location.line;
let col = info.location.col;
let msg = match info.payload.downcast_ref::<&'static str>() {
Some(s) => *s,
None => match info.payload.downcast_ref::<String>() {
Some(s) => &s[..],
None => "Box<Any>",
}
};
// 3DS-specific code begins here
use libctru::{consoleDebugInit, debugDevice};
use sys::stdio::Stderr;
let mut err = Stderr::new().ok();
let thread = thread_info::current_thread(); let thread = thread_info::current_thread();
let name = thread.as_ref() let name = thread.as_ref()
.and_then(|t| t.name()) .and_then(|t| t.name())
.unwrap_or(if threadGetCurrent() == ptr::null_mut() {"main"} else {"<unnamed>"}); .unwrap_or("<unnamed>");
unsafe {
consoleDebugInit(debugDevice::debugDevice_CONSOLE);
}
println!("thread '{}' panicked at '{}', {}:{}:{}", let write = |err: &mut ::io::Write| {
let _ = writeln!(err, "thread '{}' panicked at '{}', {}:{}:{}",
name, msg, file, line, col); name, msg, file, line, col);
// Citra seems to ignore calls to svcExitProcess, and libc::abort() #[cfg(feature = "backtrace")]
// causes it to lock up and fill the console with endless debug statements. {
// So instead of terminating the program, we just let it spin in the following use sync::atomic::{AtomicBool, Ordering};
// loop. This means that any background threads might continue to run after
// this thread panics, but there's not a lot we can do about that currently. static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
loop { }
if let Some(format) = log_backtrace {
let _ = backtrace::print(err, format);
} else if FIRST_PANIC.compare_and_swap(true, false, Ordering::SeqCst) {
let _ = writeln!(err, "note: Run with `RUST_BACKTRACE=1` for a backtrace.");
}
}
};
let prev = LOCAL_STDERR.with(|s| s.borrow_mut().take());
match (prev, err.as_mut()) {
(Some(mut stderr), _) => {
write(&mut *stderr);
let mut s = Some(stderr);
LOCAL_STDERR.with(|slot| {
*slot.borrow_mut() = s.take();
});
}
(None, Some(ref mut err)) => { write(err) }
_ => {}
} }
} }
#[cfg(not(feature = "citra"))] #[cfg(not(feature = "citra"))]
#[unstable(feature = "libstd_sys_internals", fn default_hook(info: &PanicInfo) {
reason = "used by the panic! macro", #[cfg(feature = "backtrace")]
issue = "0")] use sys_common::backtrace;
#[inline(never)] #[cold]
pub fn begin_panic<M: Any + Send + Display>(msg: M, file_line_col: &(&'static str, u32, u32)) -> ! { // If this is a double panic, make sure that we print a backtrace
let msg = Box::new(msg); // for this panic. Otherwise only print it if logging is enabled.
let (file, line, col) = *file_line_col; #[cfg(feature = "backtrace")]
let log_backtrace = {
let panics = update_panic_count(0);
if panics >= 2 {
Some(backtrace::PrintFormat::Full)
} else {
backtrace::log_enabled()
}
};
let file = info.location.file;
let line = info.location.line;
let col = info.location.col;
let msg = match info.payload.downcast_ref::<&'static str>() {
Some(s) => *s,
None => match info.payload.downcast_ref::<String>() {
Some(s) => &s[..],
None => "Box<Any>",
}
};
// 3DS-specific code begins here // 3DS-specific code begins here
use libctru::{errorInit, errorText, errorDisp, use libctru::{errorInit, errorText, errorDisp,
APT_HardwareResetAsync, svcExitProcess, threadGetCurrent,
errorConf, errorType, CFG_Language}; errorConf, errorType, CFG_Language};
use libc; use libc;
unsafe {
// Determine thread name
let thread = thread_info::current_thread(); let thread = thread_info::current_thread();
let name = thread.as_ref() let name = thread.as_ref()
.and_then(|t| t.name()) .and_then(|t| t.name())
.unwrap_or(if threadGetCurrent() == ptr::null_mut() {"main"} else {"<unnamed>"}); .unwrap_or("<unnamed>");
unsafe {
// Setup error payload // Setup error payload
let error_text = format!("thread '{}' panicked at '{}', {}:{}:{}", let error_text = format!("thread '{}' panicked at '{}', {}:{}:{}",
name, msg, file, line, col); name, msg, file, line, col);
let mut error_conf: errorConf = mem::uninitialized(); let mut error_conf: errorConf = mem::uninitialized();
errorInit(&mut error_conf, errorInit(&mut error_conf,
errorType::ERROR_TEXT_WORD_WRAP, errorType::ERROR_TEXT_WORD_WRAP,
@ -134,16 +453,26 @@ pub fn begin_panic<M: Any + Send + Display>(msg: M, file_line_col: &(&'static st
// Display error // Display error
errorDisp(&mut error_conf); errorDisp(&mut error_conf);
}
}
// Now that we're all done printing, it's time to exit the program. #[cfg(not(test))]
// We don't have stack unwinding yet, so let's just trigger a reboot #[doc(hidden)]
APT_HardwareResetAsync(); #[unstable(feature = "update_panic_count", issue = "0")]
pub fn update_panic_count(amt: isize) -> usize {
use cell::Cell;
thread_local! { static PANIC_COUNT: Cell<usize> = Cell::new(0) }
// If rebooting fails for some reason, we extra-forcibly end the program PANIC_COUNT.with(|c| {
svcExitProcess() let next = (c.get() as isize + amt) as usize;
} c.set(next);
return next
})
} }
#[cfg(test)]
pub use realstd::rt::update_panic_count;
/// Invoke a closure, capturing the cause of an unwinding panic if one occurs. /// 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>> { pub unsafe fn try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<Any + Send>> {
#[allow(unions_with_drop_fields)] #[allow(unions_with_drop_fields)]
@ -209,29 +538,129 @@ pub unsafe fn try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<Any + Send>> {
} }
} }
#[cfg(not(test))] /// Determines whether the current thread is unwinding because of panic.
#[doc(hidden)] pub fn panicking() -> bool {
#[unstable(feature = "update_panic_count", issue = "0")] update_panic_count(0) != 0
pub fn update_panic_count(amt: isize) -> usize { }
use cell::Cell;
thread_local! { static PANIC_COUNT: Cell<usize> = Cell::new(0) }
PANIC_COUNT.with(|c| { /// Entry point of panic from the libcore crate.
let next = (c.get() as isize + amt) as usize; #[lang = "panic_fmt"]
c.set(next); #[unwind]
return next pub extern fn rust_begin_panic(msg: fmt::Arguments,
}) file: &'static str,
line: u32,
col: u32) -> ! {
begin_panic_fmt(&msg, &(file, line, col))
} }
// *Implementation borrowed from the libpanic_abort crate* /// The entry point for panicking with a formatted message.
// ///
// Rust's "try" function, but if we're aborting on panics we just call the /// This is designed to reduce the amount of code required at the call
// function as there's nothing else we need to do here. /// site as much as possible (so that `panic!()` has as low an impact
#[allow(improper_ctypes)] /// on (e.g.) the inlining of other functions as possible), by moving
extern fn __rust_maybe_catch_panic(f: fn(*mut u8), /// the actual formatting into this shared place.
data: *mut u8, #[unstable(feature = "libstd_sys_internals",
_data_ptr: *mut usize, reason = "used by the panic! macro",
_vtable_ptr: *mut usize) -> u32 { issue = "0")]
f(data); #[inline(never)] #[cold]
0 pub fn begin_panic_fmt(msg: &fmt::Arguments,
file_line_col: &(&'static str, u32, 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_col)
}
/// 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_col: &(&'static str, u32, 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_col)
}
/// 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_col: &(&'static str, u32, u32)) -> ! {
let (file, line, col) = *file_line_col;
let panics = update_panic_count(1);
// 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 > 2 {
util::dumb_print(format_args!("thread panicked while processing \
panic. aborting.\n"));
unsafe { intrinsics::abort() }
}
unsafe {
let info = PanicInfo {
payload: &*msg,
location: Location {
file,
line,
col,
},
};
HOOK_LOCK.read();
match HOOK {
Hook::Default => default_hook(&info),
Hook::Custom(ptr) => (*ptr)(&info),
}
HOOK_LOCK.read_unlock();
}
if panics > 1 {
// If a thread panics while it's already unwinding then we
// have limited options. Currently our preference is to
// just abort. In the future we may consider resuming
// unwinding or otherwise exiting the thread cleanly.
util::dumb_print(format_args!("thread panicked while panicking. \
aborting.\n"));
unsafe { intrinsics::abort() }
}
rust_panic(msg)
}
/// Shim around rust_panic. Called by resume_unwind.
pub fn update_count_then_panic(msg: Box<Any + Send>) -> ! {
update_panic_count(1);
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)
} }

18
ctr-std/src/rt.rs

@ -22,6 +22,9 @@
issue = "0")] issue = "0")]
#![doc(hidden)] #![doc(hidden)]
use panic;
use sys_common::thread_info;
use thread::Thread;
use mem; use mem;
// Reexport some of our utilities which are expected by other crates. // Reexport some of our utilities which are expected by other crates.
@ -31,6 +34,19 @@ pub use panicking::{begin_panic, begin_panic_fmt};
#[lang = "start"] #[lang = "start"]
#[allow(unused_variables)] #[allow(unused_variables)]
fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize { fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize {
unsafe { mem::transmute::<_, fn()>(main)(); } let failed = unsafe {
let thread = Thread::new(Some("main".to_owned()));
thread_info::set(None, thread);
let res = panic::catch_unwind(mem::transmute::<_, fn()>(main));
res.is_err()
};
if failed {
101
} else {
0 0
}
} }

4
ctr-std/src/sys_common/mod.rs

@ -77,6 +77,10 @@ pub fn at_exit<F: FnOnce() + Send + 'static>(f: F) -> Result<(), ()> {
if at_exit_imp::push(Box::new(f)) {Ok(())} else {Err(())} if at_exit_imp::push(Box::new(f)) {Ok(())} else {Err(())}
} }
macro_rules! rtabort {
($($t:tt)*) => (::sys_common::util::abort(format_args!($($t)*)))
}
// Computes (value*numer)/denom without overflow, as long as both // Computes (value*numer)/denom without overflow, as long as both
// (numer*denom) and the overall result fit into i64 (which is the case // (numer*denom) and the overall result fit into i64 (which is the case
// for our time conversions). // for our time conversions).

3
examples/.cargo/config

@ -10,6 +10,9 @@ rustflags = [
"-Clink-arg=-mtune=mpcore", "-Clink-arg=-mtune=mpcore",
"-Clink-arg=-mfpu=vfp", "-Clink-arg=-mfpu=vfp",
"-Clink-arg=-mtp=soft", "-Clink-arg=-mtp=soft",
"-Clink-arg=-z",
"-Clink-arg=muldefs",
"-Clink-arg=-lgcc",
"-Clink-arg=-lsysbase", "-Clink-arg=-lsysbase",
"-Clink-arg=-lc" "-Clink-arg=-lc"
] ]

1
examples/3ds.json

@ -14,6 +14,5 @@
"relocation-model": "static", "relocation-model": "static",
"executables": true, "executables": true,
"exe-suffix": ".elf", "exe-suffix": ".elf",
"panic-strategy": "abort",
"linker-flavor": "gcc" "linker-flavor": "gcc"
} }

4
examples/Cargo.toml

@ -5,5 +5,9 @@ version = "0.1.0"
[dependencies] [dependencies]
ctru-rs = { path = "../ctru-rs" } ctru-rs = { path = "../ctru-rs" }
[profile.dev]
lto = true
[profile.release] [profile.release]
lto = true lto = true

2
examples/Xargo.toml

@ -1,5 +1,7 @@
[dependencies.collections] [dependencies.collections]
[dependencies.rand] [dependencies.rand]
[dependencies.panic_abort]
[dependencies.panic_unwind]
[dependencies.std] [dependencies.std]
path = "../ctr-std" path = "../ctr-std"

Loading…
Cancel
Save