sel4_panicking_env/
lib.rs

1//
2// Copyright 2023, Colias Group, LLC
3//
4// SPDX-License-Identifier: BSD-2-Clause
5//
6
7#![no_std]
8#![feature(linkage)]
9
10use core::fmt;
11use core::panic::Location;
12use core::str;
13
14unsafe extern "Rust" {
15    fn __sel4_panicking_env__debug_put_char(c: u8);
16    fn __sel4_panicking_env__abort_hook(info: Option<&AbortInfo>);
17    fn __sel4_panicking_env__abort_trap() -> !;
18}
19
20/// Registers a function to be used by [`debug_put_char`], [`debug_print!`], and [`debug_println!`].
21///
22/// This macro uses the function `$path` to define the following symbol:
23///
24/// ```
25/// unsafe extern "Rust" {
26///     fn __sel4_panicking_env__debug_put_char(c: u8);
27/// }
28/// ```
29#[macro_export]
30macro_rules! register_debug_put_char {
31    ($(#[$attrs:meta])* $path:path) => {
32        #[allow(non_snake_case)]
33        const _: () = {
34            $(#[$attrs])*
35            #[unsafe(no_mangle)]
36            fn __sel4_panicking_env__debug_put_char(c: u8) {
37                const F: fn(u8) = $path;
38                F(c)
39            }
40        };
41    };
42}
43
44/// Registers an abort hook to be used by [`abort!`] and [`abort_without_info`].
45///
46/// This macro uses the function `$path` to define the following symbol:
47///
48/// ```no_run
49/// use sel4_panicking_env::AbortInfo;
50///
51/// unsafe extern "Rust" {
52///     fn __sel4_panicking_env__abort_hook(info: Option<&AbortInfo>);
53/// }
54/// ```
55#[macro_export]
56macro_rules! register_abort_hook {
57    ($(#[$attrs:meta])* $path:path) => {
58        #[allow(non_snake_case)]
59        const _: () = {
60            $(#[$attrs])*
61            #[unsafe(no_mangle)]
62            fn __sel4_panicking_env__abort_hook(info: ::core::option::Option<&$crate::AbortInfo>) {
63                const F: fn(::core::option::Option<&$crate::AbortInfo>) = $path;
64                F(info)
65            }
66        };
67    };
68}
69
70register_abort_hook!(
71    #[linkage = "weak"]
72    default_abort_hook
73);
74
75fn default_abort_hook(info: Option<&AbortInfo>) {
76    match info {
77        Some(info) => debug_println!("{}", info),
78        None => debug_println!("(aborted)"),
79    }
80}
81
82/// Registers an abort trap to be used by [`abort!`] and [`abort_without_info`].
83///
84/// This macro uses the function `$path` to define the following symbol:
85///
86/// ```
87/// unsafe extern "Rust" {
88///     fn __sel4_panicking_env__abort_trap() -> !;
89/// }
90/// ```
91#[macro_export]
92macro_rules! register_abort_trap {
93    ($(#[$attrs:meta])* $path:path) => {
94        #[allow(non_snake_case)]
95        const _: () = {
96            $(#[$attrs])*
97            #[unsafe(no_mangle)]
98            fn __sel4_panicking_env__abort_trap() -> ! {
99                const F: fn() -> ! = $path;
100                F()
101            }
102        };
103    };
104}
105
106// // //
107
108/// Prints via a link-time hook.
109///
110/// This function uses the following externally defined symbol:
111///
112/// ```
113/// unsafe extern "Rust" {
114///     fn __sel4_panicking_env__debug_put_char(c: u8);
115/// }
116/// ```
117///
118/// [`register_debug_put_char`] provides a typesafe way to define that symbol.
119pub fn debug_put_char(c: u8) {
120    unsafe { __sel4_panicking_env__debug_put_char(c) }
121}
122
123struct DebugWrite;
124
125impl fmt::Write for DebugWrite {
126    fn write_str(&mut self, s: &str) -> fmt::Result {
127        for &c in s.as_bytes() {
128            debug_put_char(c)
129        }
130        Ok(())
131    }
132}
133
134#[doc(hidden)]
135pub fn __debug_print_macro_helper(args: fmt::Arguments) {
136    fmt::write(&mut DebugWrite, args).unwrap_or_else(|err| {
137        // Just report error. This function must not fail.
138        let _ = fmt::write(&mut DebugWrite, format_args!("({err})"));
139    })
140}
141
142/// Like `std::print!`, except backed by [`debug_put_char`].
143#[macro_export]
144macro_rules! debug_print {
145    ($($arg:tt)*) => ($crate::__debug_print_macro_helper(format_args!($($arg)*)));
146}
147
148/// Like `std::println!`, except backed by [`debug_put_char`].
149#[macro_export]
150macro_rules! debug_println {
151    () => ($crate::debug_println!(""));
152    ($($arg:tt)*) => ($crate::debug_print!("{}\n", format_args!($($arg)*)));
153}
154
155// // //
156
157/// Information about an abort passed to an abort hook.
158pub struct AbortInfo<'a> {
159    message: Option<&'a fmt::Arguments<'a>>,
160    location: Option<&'a Location<'a>>,
161}
162
163impl AbortInfo<'_> {
164    /// The `core::fmt::Arguments` with which [`abort!`] was called.
165    pub fn message(&self) -> Option<&fmt::Arguments<'_>> {
166        self.message
167    }
168
169    /// The location at which [`abort!`] was called.
170    pub fn location(&self) -> Option<&Location<'_>> {
171        self.location
172    }
173}
174
175impl fmt::Display for AbortInfo<'_> {
176    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
177        f.write_str("aborted at ")?;
178        if let Some(location) = self.location {
179            location.fmt(f)?;
180        } else {
181            f.write_str("unknown location")?;
182        }
183        if let Some(message) = self.message {
184            f.write_str(":\n")?;
185            f.write_fmt(*message)?;
186        }
187        Ok(())
188    }
189}
190
191fn abort(info: Option<&AbortInfo>) -> ! {
192    unsafe {
193        __sel4_panicking_env__abort_hook(info);
194        __sel4_panicking_env__abort_trap()
195    }
196}
197
198/// Aborts without any [`AbortInfo`].
199///
200/// This function does the same thing as [`abort!`], except it passes `None` to the abort hook.
201pub fn abort_without_info() -> ! {
202    abort(None)
203}
204
205#[doc(hidden)]
206#[track_caller]
207pub fn __abort_macro_helper(message: Option<fmt::Arguments>) -> ! {
208    abort(Some(&AbortInfo {
209        message: message.as_ref(),
210        location: Some(Location::caller()),
211    }))
212}
213
214/// Aborts execution with a message.
215///
216/// [`abort!`] accepts the same patterns `core::panic!`:
217///
218/// ```no_run
219/// use sel4_panicking_env::abort;
220///
221/// abort!();
222/// abort!("uh oh!");
223/// abort!("uh {} {}!", 123, "oh");
224/// ```
225///
226/// This macro first invokes an externally defined abort hook which is resolved at link time, and
227/// then calls `core::intrinsics::abort()`.
228///
229/// The following externally defined symbol is used as the abort hook:
230///
231/// ```
232/// use sel4_panicking_env::AbortInfo;
233///
234/// unsafe extern "Rust" {
235///     fn __sel4_panicking_env__abort_hook(info: Option<&AbortInfo>);
236/// }
237/// ```
238///
239/// The [`sel4_panicking_env` crate](crate) defines a weak version of this symbol which just prints
240/// the [`AbortInfo`] argument using [`debug_print!`].
241///
242/// [`register_abort_hook`] provides a typesafe way to define that symbol.
243#[macro_export]
244macro_rules! abort {
245    () => ($crate::__abort_macro_helper(::core::option::Option::None));
246    ($($arg:tt)*) => ($crate::__abort_macro_helper(::core::option::Option::Some(format_args!($($arg)*))));
247}