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