Browse Source

Add unwinding strategy for panics

pull/10/head
Fenrir 8 years ago
parent
commit
5f0fc0c001
  1. 6
      ctr-std/src/collections/hash/table.rs
  2. 21
      ctr-std/src/lib.rs
  3. 11
      ctr-std/src/panic.rs
  4. 616
      ctr-std/src/panicking.rs
  5. 5
      ctr-std/src/rt.rs
  6. 4
      ctr-std/src/sys_common/mod.rs
  7. 3
      examples/.cargo/config
  8. 1
      examples/3ds.json
  9. 4
      examples/Cargo.toml
  10. 2
      examples/Xargo.toml

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

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

21
ctr-std/src/lib.rs

@ -26,6 +26,7 @@ @@ -26,6 +26,7 @@
#![feature(lang_items)]
#![feature(macro_reexport)]
#![feature(needs_drop)]
#![feature(needs_panic_runtime)]
#![feature(oom)]
#![feature(on_unimplemented)]
#![feature(optin_builtin_traits)]
@ -45,10 +46,13 @@ @@ -45,10 +46,13 @@
#![feature(unicode)]
#![feature(unique)]
#![feature(untagged_unions)]
#![feature(unwind_attributes)]
#![feature(zero_one)]
#![allow(non_camel_case_types, dead_code, unused_features)]
#![no_std]
#![needs_panic_runtime]
#![cfg_attr(not(stage0), default_lib_allocator)]
#![stable(feature = "rust1", since = "1.0.0")]
@ -197,20 +201,3 @@ pub mod rt; @@ -197,20 +201,3 @@ pub mod rt;
pub mod __rand {
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; @@ -22,8 +22,8 @@ use rc::Rc;
use sync::{Arc, Mutex, RwLock, atomic};
use thread::Result;
//#[stable(feature = "panic_hooks", since = "1.10.0")]
//pub use panicking::{take_hook, set_hook, PanicInfo, Location};
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub use panicking::{take_hook, set_hook, PanicInfo, Location};
/// A marker trait which represents "panic safe" types in Rust.
///
@ -112,7 +112,7 @@ pub trait UnwindSafe {} @@ -112,7 +112,7 @@ pub trait UnwindSafe {}
/// This is a "helper marker trait" used to provide impl blocks for the
/// `UnwindSafe` trait, for more information see that documentation.
#[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 \
across a catch_unwind boundary"]
pub trait RefUnwindSafe {}
@ -388,7 +388,6 @@ pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> { @@ -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")]
// we always abort so I'm pretty sure there's no reason to ever call this
pub fn resume_unwind(_payload: Box<Any + Send>) -> ! {
unimplemented!()
pub fn resume_unwind(payload: Box<Any + Send>) -> ! {
panicking::update_count_then_panic(payload)
}

616
ctr-std/src/panicking.rs

@ -10,16 +10,26 @@ @@ -10,16 +10,26 @@
//! 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 io::prelude::*;
use any::Any;
use cell::RefCell;
use fmt::{self, Display};
use fmt;
use intrinsics;
use mem;
use ptr;
use raw;
use sys_common::rwlock::RWLock;
use sys_common::thread_info;
use sys_common::util;
use thread;
thread_local! {
pub static LOCAL_STDERR: RefCell<Option<Box<Write + Send>>> = {
@ -27,97 +37,416 @@ thread_local! { @@ -27,97 +37,416 @@ thread_local! {
}
}
///The compiler wants this to be here. Otherwise it won't be happy. And we like happy compilers.
#[lang = "eh_personality"]
pub extern fn eh_personality() {}
#[allow(improper_ctypes)]
extern {
fn __rust_maybe_catch_panic(f: fn(*mut u8),
data: *mut u8,
data_ptr: *mut usize,
vtable_ptr: *mut usize) -> u32;
#[unwind]
fn __rust_start_panic(data: usize, vtable: usize) -> u32;
}
/// Determines whether the current thread is unwinding because of panic.
// Which it won't be, since we still don't have stack unwinding.
pub fn panicking() -> bool {
false
#[derive(Copy, Clone)]
enum Hook {
Default,
Custom(*mut (Fn(&PanicInfo) + 'static + Sync + Send)),
}
/// Entry point of panic from the libcore crate.
#[lang = "panic_fmt"]
pub extern fn rust_begin_panic(msg: fmt::Arguments,
file: &'static str,
line: u32,
col: u32) -> ! {
begin_panic_fmt(&msg, &(file, line, col))
static HOOK_LOCK: RWLock = RWLock::new();
static mut HOOK: Hook = Hook::Default;
/// Registers a custom panic hook, replacing any that was previously registered.
///
/// The panic hook is invoked when a thread panics, but before the panic runtime
/// 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
/// 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_col: &(&'static str, u32, u32)) -> ! {
use fmt::Write;
/// If no custom hook is registered, the default hook will be returned.
///
/// # Panics
///
/// Panics if called from a panicking thread.
///
/// # Examples
///
/// The following will print "Normal panic":
///
/// ```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();
let _ = s.write_fmt(*msg);
begin_panic(s, file_line_col);
unsafe {
HOOK_LOCK.write();
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")]
#[unstable(feature = "libstd_sys_internals",
reason = "used by the panic! macro",
issue = "0")]
#[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;
fn default_hook(info: &PanicInfo) {
#[cfg(feature = "backtrace")]
use sys_common::backtrace;
// If this is a double panic, make sure that we print a backtrace
// for this panic. Otherwise only print it if logging is enabled.
#[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
use libctru::{consoleInit, consoleClear, gfxScreen_t, threadGetCurrent};
use libctru::{consoleInit, consoleDebugInit, debugDevice,
consoleClear, gfxScreen_t, threadGetCurrent};
use sys::stdio::Stderr;
let mut err = Stderr::new().ok();
let thread = thread_info::current_thread();
let name;
unsafe {
// Set up a new console, overwriting whatever was on the top screen
// Set up a new console, overwriting whatever was on the bottom screen
// before we started panicking
let _console = consoleInit(gfxScreen_t::GFX_TOP, ptr::null_mut());
let _console = consoleInit(gfxScreen_t::GFX_BOTTOM, ptr::null_mut());
consoleClear();
consoleDebugInit(debugDevice::debugDevice_CONSOLE);
// Determine thread name
let thread = thread_info::current_thread();
let name = thread.as_ref()
name = thread.as_ref()
.and_then(|t| t.name())
.unwrap_or(if threadGetCurrent() == ptr::null_mut() {"main"} else {"<unnamed>"});
}
let write = |err: &mut ::io::Write| {
let _ = writeln!(err, "thread '{}' panicked at '{}', {}:{}:{}",
name, msg, file, line, col);
println!("thread '{}' panicked at '{}', {}:{}:{}",
name, msg, file, line, col);
#[cfg(feature = "backtrace")]
{
use sync::atomic::{AtomicBool, Ordering};
// Citra seems to ignore calls to svcExitProcess, and libc::abort()
// 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
// 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.
loop { }
static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
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) }
_ => {}
}
// Citra will crash and die horribly if we allow it to go further than this,
// So we just trap control flow in this loop instead. You'll still see the panic
// message, and Citra won't die quite as horribly. It's a win-win! Well, mostly.
loop { }
}
#[cfg(not(feature = "citra"))]
#[unstable(feature = "libstd_sys_internals",
reason = "used by the panic! macro",
issue = "0")]
#[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;
fn default_hook(info: &PanicInfo) {
#[cfg(feature = "backtrace")]
use sys_common::backtrace;
// If this is a double panic, make sure that we print a backtrace
// for this panic. Otherwise only print it if logging is enabled.
#[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
use libctru::{errorInit, errorText, errorDisp,
APT_HardwareResetAsync, svcExitProcess, threadGetCurrent,
use libctru::{errorInit, errorText, errorDisp, threadGetCurrent,
errorConf, errorType, CFG_Language};
use libc;
unsafe {
// Determine thread name
let thread = thread_info::current_thread();
let name = thread.as_ref()
.and_then(|t| t.name())
@ -126,6 +455,7 @@ pub fn begin_panic<M: Any + Send + Display>(msg: M, file_line_col: &(&'static st @@ -126,6 +455,7 @@ pub fn begin_panic<M: Any + Send + Display>(msg: M, file_line_col: &(&'static st
// Setup error payload
let error_text = format!("thread '{}' panicked at '{}', {}:{}:{}",
name, msg, file, line, col);
let mut error_conf: errorConf = mem::uninitialized();
errorInit(&mut error_conf,
errorType::ERROR_TEXT_WORD_WRAP,
@ -134,16 +464,26 @@ pub fn begin_panic<M: Any + Send + Display>(msg: M, file_line_col: &(&'static st @@ -134,16 +464,26 @@ pub fn begin_panic<M: Any + Send + Display>(msg: M, file_line_col: &(&'static st
// Display error
errorDisp(&mut error_conf);
}
}
// Now that we're all done printing, it's time to exit the program.
// We don't have stack unwinding yet, so let's just trigger a reboot
APT_HardwareResetAsync();
#[cfg(not(test))]
#[doc(hidden)]
#[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
svcExitProcess()
}
PANIC_COUNT.with(|c| {
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.
pub unsafe fn try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<Any + Send>> {
#[allow(unions_with_drop_fields)]
@ -209,29 +549,129 @@ pub unsafe fn try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<Any + Send>> { @@ -209,29 +549,129 @@ pub unsafe fn try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<Any + Send>> {
}
}
#[cfg(not(test))]
#[doc(hidden)]
#[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) }
/// Determines whether the current thread is unwinding because of panic.
pub fn panicking() -> bool {
update_panic_count(0) != 0
}
PANIC_COUNT.with(|c| {
let next = (c.get() as isize + amt) as usize;
c.set(next);
return next
})
/// Entry point of panic from the libcore crate.
#[lang = "panic_fmt"]
#[unwind]
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*
//
// 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.
#[allow(improper_ctypes)]
extern fn __rust_maybe_catch_panic(f: fn(*mut u8),
data: *mut u8,
_data_ptr: *mut usize,
_vtable_ptr: *mut usize) -> u32 {
f(data);
0
/// 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_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)
}

5
ctr-std/src/rt.rs

@ -22,6 +22,7 @@ @@ -22,6 +22,7 @@
issue = "0")]
#![doc(hidden)]
use panic;
use mem;
// Reexport some of our utilities which are expected by other crates.
@ -31,6 +32,8 @@ pub use panicking::{begin_panic, begin_panic_fmt}; @@ -31,6 +32,8 @@ pub use panicking::{begin_panic, begin_panic_fmt};
#[lang = "start"]
#[allow(unused_variables)]
fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize {
unsafe { mem::transmute::<_, fn()>(main)(); }
let _ = unsafe {
panic::catch_unwind(mem::transmute::<_, fn()>(main))
};
0
}

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

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

3
examples/.cargo/config

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

1
examples/3ds.json

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

4
examples/Cargo.toml

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

2
examples/Xargo.toml

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

Loading…
Cancel
Save