| 1 | //! # **P**ortable **S**tack **M**anipulation | 
| 2 | //! This crate provides portable functions to control the stack pointer and inspect the properties | 
|---|
| 3 | //! of the stack. This crate does not attempt to provide safe abstractions to any operations, the | 
|---|
| 4 | //! only goals are correctness, portability and efficiency (in that exact order).  As a consequence | 
|---|
| 5 | //! most functions you will find in this crate are unsafe. | 
|---|
| 6 | //! | 
|---|
| 7 | //! Note, that the stack allocation is left up to the user. Unless you’re writing a safe | 
|---|
| 8 | //! abstraction over stack manipulation, this is unlikely to be the crate you want. Instead | 
|---|
| 9 | //! consider one of the safe abstractions over this crate such as `stacker`. Another good place to | 
|---|
| 10 | //! look at is the crates.io’s reverse dependency list. | 
|---|
| 11 |  | 
|---|
| 12 | #![ allow(unused_macros)] | 
|---|
| 13 | #![ no_std] | 
|---|
| 14 |  | 
|---|
| 15 | macro_rules! extern_item { | 
|---|
| 16 | (unsafe $($toks: tt)+) => { | 
|---|
| 17 | unsafe extern "C"$($toks)+ | 
|---|
| 18 | }; | 
|---|
| 19 | ($($toks: tt)+) => { | 
|---|
| 20 | extern "C"$($toks)+ | 
|---|
| 21 | }; | 
|---|
| 22 | } | 
|---|
| 23 |  | 
|---|
| 24 | // Surprising: turns out subsequent macro_rules! override previous definitions, instead of | 
|---|
| 25 | // erroring? Convenient for us in this case, though. | 
|---|
| 26 | #[ cfg(target_arch = "x86_64")] | 
|---|
| 27 | macro_rules! extern_item { | 
|---|
| 28 | (unsafe $($toks: tt)+) => { | 
|---|
| 29 | unsafe extern "sysv64"$($toks)+ | 
|---|
| 30 | }; | 
|---|
| 31 | ($($toks: tt)+) => { | 
|---|
| 32 | extern "sysv64"$($toks)+ | 
|---|
| 33 | }; | 
|---|
| 34 | } | 
|---|
| 35 |  | 
|---|
| 36 | #[ cfg(target_arch = "x86")] | 
|---|
| 37 | macro_rules! extern_item { | 
|---|
| 38 | (unsafe $($toks: tt)+) => { | 
|---|
| 39 | unsafe extern "fastcall"$($toks)+ | 
|---|
| 40 | }; | 
|---|
| 41 | ($($toks: tt)+) => { | 
|---|
| 42 | extern "fastcall"$($toks)+ | 
|---|
| 43 | }; | 
|---|
| 44 | } | 
|---|
| 45 |  | 
|---|
| 46 | #[ cfg(target_arch = "arm")] | 
|---|
| 47 | macro_rules! extern_item { | 
|---|
| 48 | (unsafe $($toks: tt)+) => { | 
|---|
| 49 | unsafe extern "aapcs"$($toks)+ | 
|---|
| 50 | }; | 
|---|
| 51 | ($($toks: tt)+) => { | 
|---|
| 52 | extern "aapcs"$($toks)+ | 
|---|
| 53 | }; | 
|---|
| 54 | } | 
|---|
| 55 |  | 
|---|
| 56 | // NB: this could be nicer across multiple blocks but we cannot do it because of | 
|---|
| 57 | // https://github.com/rust-lang/rust/issues/65847 | 
|---|
| 58 | extern_item! { { | 
|---|
| 59 | #![ cfg_attr(link_asm, link(name= "psm_s"))] | 
|---|
| 60 |  | 
|---|
| 61 | #[ cfg(asm)] | 
|---|
| 62 | fn rust_psm_stack_direction() -> u8; | 
|---|
| 63 | #[ cfg(asm)] | 
|---|
| 64 | fn rust_psm_stack_pointer() -> *mut u8; | 
|---|
| 65 |  | 
|---|
| 66 | #[ cfg(all(switchable_stack, not(target_os = "windows")))] | 
|---|
| 67 | #[ link_name= "rust_psm_replace_stack"] | 
|---|
| 68 | fn _rust_psm_replace_stack( | 
|---|
| 69 | data: usize, | 
|---|
| 70 | callback: extern_item!(unsafe fn(usize) -> !), | 
|---|
| 71 | sp: *mut u8 | 
|---|
| 72 | ) -> !; | 
|---|
| 73 | #[ cfg(all(switchable_stack, not(target_os = "windows")))] | 
|---|
| 74 | #[ link_name= "rust_psm_on_stack"] | 
|---|
| 75 | fn _rust_psm_on_stack( | 
|---|
| 76 | data: usize, | 
|---|
| 77 | return_ptr: usize, | 
|---|
| 78 | callback: extern_item!(unsafe fn(usize, usize)), | 
|---|
| 79 | sp: *mut u8, | 
|---|
| 80 | ); | 
|---|
| 81 | #[ cfg(all(switchable_stack, target_os = "windows"))] | 
|---|
| 82 | fn rust_psm_replace_stack( | 
|---|
| 83 | data: usize, | 
|---|
| 84 | callback: extern_item!(unsafe fn(usize) -> !), | 
|---|
| 85 | sp: *mut u8, | 
|---|
| 86 | stack_base: *mut u8 | 
|---|
| 87 | ) -> !; | 
|---|
| 88 | #[ cfg(all(switchable_stack, target_os = "windows"))] | 
|---|
| 89 | fn rust_psm_on_stack( | 
|---|
| 90 | data: usize, | 
|---|
| 91 | return_ptr: usize, | 
|---|
| 92 | callback: extern_item!(unsafe fn(usize, usize)), | 
|---|
| 93 | sp: *mut u8, | 
|---|
| 94 | stack_base: *mut u8 | 
|---|
| 95 | ); | 
|---|
| 96 | } } | 
|---|
| 97 |  | 
|---|
| 98 | #[ cfg(all(switchable_stack, not(target_os = "windows")))] | 
|---|
| 99 | #[ inline(always)] | 
|---|
| 100 | unsafe fn rust_psm_replace_stack( | 
|---|
| 101 | data: usize, | 
|---|
| 102 | callback: extern_item!(unsafe fn(usize) -> !), | 
|---|
| 103 | sp: *mut u8, | 
|---|
| 104 | _: *mut u8, | 
|---|
| 105 | ) -> ! { | 
|---|
| 106 | _rust_psm_replace_stack(data, callback, sp) | 
|---|
| 107 | } | 
|---|
| 108 |  | 
|---|
| 109 | #[ cfg(all(switchable_stack, not(target_os = "windows")))] | 
|---|
| 110 | #[ inline(always)] | 
|---|
| 111 | unsafe fn rust_psm_on_stack( | 
|---|
| 112 | data: usize, | 
|---|
| 113 | return_ptr: usize, | 
|---|
| 114 | callback: extern_item!(unsafe fn(usize, usize)), | 
|---|
| 115 | sp: *mut u8, | 
|---|
| 116 | _: *mut u8, | 
|---|
| 117 | ) { | 
|---|
| 118 | _rust_psm_on_stack(data, return_ptr, callback, sp) | 
|---|
| 119 | } | 
|---|
| 120 |  | 
|---|
| 121 | /// Run the closure on the provided stack. | 
|---|
| 122 | /// | 
|---|
| 123 | /// Once the closure completes its execution, the original stack pointer is restored and execution | 
|---|
| 124 | /// returns to the caller. | 
|---|
| 125 | /// | 
|---|
| 126 | /// `base` address must be the low address of the stack memory region, regardless of the stack | 
|---|
| 127 | /// growth direction. It is not necessary for the whole region `[base; base + size]` to be usable | 
|---|
| 128 | /// at the time this function called, however it is required that at least the following hold: | 
|---|
| 129 | /// | 
|---|
| 130 | /// * Both `base` and `base + size` are aligned up to the target-specific requirements; | 
|---|
| 131 | /// * Depending on `StackDirection` value for the platform, the end of the stack memory region, | 
|---|
| 132 | ///   which would end up containing the first frame(s), must have sufficient number of pages | 
|---|
| 133 | ///   allocated to execute code until more pages are commited. The other end should contain a guard | 
|---|
| 134 | ///   page (not writable, readable or executable) to ensure Rust’s soundness guarantees. | 
|---|
| 135 | /// | 
|---|
| 136 | /// Note, that some or all of these considerations are irrelevant to some applications. For | 
|---|
| 137 | /// example, Rust’s soundness story relies on all stacks having a guard-page, however if the user | 
|---|
| 138 | /// is able to guarantee that the memory region used for stack cannot be exceeded, a guard page may | 
|---|
| 139 | /// end up being an expensive unnecessity. | 
|---|
| 140 | /// | 
|---|
| 141 | /// The previous stack may not be deallocated. If an ability to deallocate the old stack is desired | 
|---|
| 142 | /// consider `replace_stack` instead. | 
|---|
| 143 | /// | 
|---|
| 144 | /// # Guidelines | 
|---|
| 145 | /// | 
|---|
| 146 | /// Memory regions that are aligned to a single page (usually 4kB) are an extremely portable choice | 
|---|
| 147 | /// for stacks. | 
|---|
| 148 | /// | 
|---|
| 149 | /// Allocate at least 4kB of stack. Some architectures (such as SPARC) consume stack memory | 
|---|
| 150 | /// significantly faster compared to the more usual architectures such as x86 or ARM. Allocating | 
|---|
| 151 | /// less than 4kB of memory may make it impossible to commit more pages without overflowing the | 
|---|
| 152 | /// stack later on. | 
|---|
| 153 | /// | 
|---|
| 154 | /// # Unsafety | 
|---|
| 155 | /// | 
|---|
| 156 | /// The stack `base` address must be aligned as appropriate for the target. | 
|---|
| 157 | /// | 
|---|
| 158 | /// The stack `size` must be a multiple of stack alignment required by target. | 
|---|
| 159 | /// | 
|---|
| 160 | /// The `size` must not overflow `isize`. | 
|---|
| 161 | /// | 
|---|
| 162 | /// `callback` must not unwind or return control flow by any other means than directly returning. | 
|---|
| 163 | /// | 
|---|
| 164 | /// # Examples | 
|---|
| 165 | /// | 
|---|
| 166 | /// ``` | 
|---|
| 167 | /// use std::alloc; | 
|---|
| 168 | /// const STACK_ALIGN: usize = 4096; | 
|---|
| 169 | /// const STACK_SIZE: usize = 4096; | 
|---|
| 170 | /// unsafe { | 
|---|
| 171 | ///     let layout = alloc::Layout::from_size_align(STACK_SIZE, STACK_ALIGN).unwrap(); | 
|---|
| 172 | ///     let new_stack = alloc::alloc(layout); | 
|---|
| 173 | ///     assert!(!new_stack.is_null(), "allocations must succeed!"); | 
|---|
| 174 | ///     let (stack, result) = psm::on_stack(new_stack, STACK_SIZE, || { | 
|---|
| 175 | ///         (psm::stack_pointer(), 4 + 4) | 
|---|
| 176 | ///     }); | 
|---|
| 177 | ///     println!( "4 + 4 = {} has been calculated on stack {:p}", result, stack); | 
|---|
| 178 | /// } | 
|---|
| 179 | /// ``` | 
|---|
| 180 | #[ cfg(switchable_stack)] | 
|---|
| 181 | pub unsafe fn on_stack<R, F: FnOnce() -> R>(base: *mut u8, size: usize, callback: F) -> R { | 
|---|
| 182 | use core::mem::MaybeUninit; | 
|---|
| 183 |  | 
|---|
| 184 | extern_item! { | 
|---|
| 185 | unsafe fn with_on_stack<R, F: FnOnce() -> R>(callback_ptr: usize, return_ptr: usize) { | 
|---|
| 186 | let return_ptr = (*(return_ptr as *mut MaybeUninit<R>)).as_mut_ptr(); | 
|---|
| 187 | let callback = (*(callback_ptr as *mut MaybeUninit<F>)).as_ptr(); | 
|---|
| 188 | // Safe to move out from `F`, because closure in is forgotten in `on_stack` and dropping | 
|---|
| 189 | // only occurs in this callback. | 
|---|
| 190 | return_ptr.write((callback.read())()); | 
|---|
| 191 | } | 
|---|
| 192 | } | 
|---|
| 193 | let sp = match StackDirection::new() { | 
|---|
| 194 | StackDirection::Ascending => base, | 
|---|
| 195 | StackDirection::Descending => base.offset(size as isize), | 
|---|
| 196 | }; | 
|---|
| 197 | let mut callback: MaybeUninit<F> = MaybeUninit::new(callback); | 
|---|
| 198 | let mut return_value: MaybeUninit<R> = MaybeUninit::uninit(); | 
|---|
| 199 | rust_psm_on_stack( | 
|---|
| 200 | &mut callback as *mut MaybeUninit<F> as usize, | 
|---|
| 201 | &mut return_value as *mut MaybeUninit<R> as usize, | 
|---|
| 202 | with_on_stack::<R, F>, | 
|---|
| 203 | sp, | 
|---|
| 204 | base, | 
|---|
| 205 | ); | 
|---|
| 206 | return return_value.assume_init(); | 
|---|
| 207 | } | 
|---|
| 208 |  | 
|---|
| 209 | /// Run the provided non-terminating computation on an entirely new stack. | 
|---|
| 210 | /// | 
|---|
| 211 | /// `base` address must be the low address of the stack memory region, regardless of the stack | 
|---|
| 212 | /// growth direction. It is not necessary for the whole region `[base; base + size]` to be usable | 
|---|
| 213 | /// at the time this function called, however it is required that at least the following hold: | 
|---|
| 214 | /// | 
|---|
| 215 | /// * Both `base` and `base + size` are aligned up to the target-specific requirements; | 
|---|
| 216 | /// * Depending on `StackDirection` value for the platform, the end of the stack memory region, | 
|---|
| 217 | ///   which would end up containing the first frame(s), must have sufficient number of pages | 
|---|
| 218 | ///   allocated to execute code until more pages are commited. The other end should contain a guard | 
|---|
| 219 | ///   page (not writable, readable or executable) to ensure Rust’s soundness guarantees. | 
|---|
| 220 | /// | 
|---|
| 221 | /// Note, that some or all of these considerations are irrelevant to some applications. For | 
|---|
| 222 | /// example, Rust’s soundness story relies on all stacks having a guard-page, however if the user | 
|---|
| 223 | /// is able to guarantee that the memory region used for stack cannot be exceeded, a guard page may | 
|---|
| 224 | /// end up being an expensive unnecessity. | 
|---|
| 225 | /// | 
|---|
| 226 | /// The previous stack is not deallocated and may not be deallocated unless the data on the old | 
|---|
| 227 | /// stack is not referenced in any way (by e.g. the `callback` closure). | 
|---|
| 228 | /// | 
|---|
| 229 | /// On platforms where multiple stack pointers are available, the “current” stack pointer is | 
|---|
| 230 | /// replaced. | 
|---|
| 231 | /// | 
|---|
| 232 | /// # Guidelines | 
|---|
| 233 | /// | 
|---|
| 234 | /// Memory regions that are aligned to a single page (usually 4kB) are an extremely portable choice | 
|---|
| 235 | /// for stacks. | 
|---|
| 236 | /// | 
|---|
| 237 | /// Allocate at least 4kB of stack. Some architectures (such as SPARC) consume stack memory | 
|---|
| 238 | /// significantly faster compared to the more usual architectures such as x86 or ARM. Allocating | 
|---|
| 239 | /// less than 4kB of memory may make it impossible to commit more pages without overflowing the | 
|---|
| 240 | /// stack later on. | 
|---|
| 241 | /// | 
|---|
| 242 | /// # Unsafety | 
|---|
| 243 | /// | 
|---|
| 244 | /// The stack `base` address must be aligned as appropriate for the target. | 
|---|
| 245 | /// | 
|---|
| 246 | /// The stack `size` must be a multiple of stack alignment required by target. | 
|---|
| 247 | /// | 
|---|
| 248 | /// The `size` must not overflow `isize`. | 
|---|
| 249 | /// | 
|---|
| 250 | /// `callback` must not return (not enforced by typesystem currently because `!` is unstable), | 
|---|
| 251 | /// unwind or otherwise return control flow to any of the previous frames. | 
|---|
| 252 | #[ cfg(switchable_stack)] | 
|---|
| 253 | pub unsafe fn replace_stack<F: FnOnce()>(base: *mut u8, size: usize, callback: F) -> ! { | 
|---|
| 254 | extern_item! { unsafe fn with_replaced_stack<F: FnOnce()>(d: usize) -> ! { | 
|---|
| 255 | // Safe to move out, because the closure is essentially forgotten by | 
|---|
| 256 | // this being required to never return... | 
|---|
| 257 | ::core::ptr::read(d as *const F)(); | 
|---|
| 258 | ::core::hint::unreachable_unchecked(); | 
|---|
| 259 | } } | 
|---|
| 260 | let sp: *mut u8 = match StackDirection::new() { | 
|---|
| 261 | StackDirection::Ascending => base, | 
|---|
| 262 | StackDirection::Descending => base.offset(count:size as isize), | 
|---|
| 263 | }; | 
|---|
| 264 | rust_psm_replace_stack( | 
|---|
| 265 | &callback as *const F as usize, | 
|---|
| 266 | callback:with_replaced_stack::<F>, | 
|---|
| 267 | sp, | 
|---|
| 268 | base, | 
|---|
| 269 | ); | 
|---|
| 270 | } | 
|---|
| 271 |  | 
|---|
| 272 | /// The direction into which stack grows as stack frames are made. | 
|---|
| 273 | /// | 
|---|
| 274 | /// This is a target-specific property that can be obtained at runtime by calling | 
|---|
| 275 | /// `StackDirection::new()`. | 
|---|
| 276 | #[ derive(Clone, Copy, PartialEq, Eq, Debug)] | 
|---|
| 277 | pub enum StackDirection { | 
|---|
| 278 | Ascending = 1, | 
|---|
| 279 | Descending = 2, | 
|---|
| 280 | } | 
|---|
| 281 |  | 
|---|
| 282 | impl StackDirection { | 
|---|
| 283 | /// Obtain the stack growth direction. | 
|---|
| 284 | #[ cfg(asm)] | 
|---|
| 285 | pub fn new() -> StackDirection { | 
|---|
| 286 | const ASC: u8 = StackDirection::Ascending as u8; | 
|---|
| 287 | const DSC: u8 = StackDirection::Descending as u8; | 
|---|
| 288 | unsafe { | 
|---|
| 289 | match rust_psm_stack_direction() { | 
|---|
| 290 | ASC => StackDirection::Ascending, | 
|---|
| 291 | DSC => StackDirection::Descending, | 
|---|
| 292 | _ => ::core::hint::unreachable_unchecked(), | 
|---|
| 293 | } | 
|---|
| 294 | } | 
|---|
| 295 | } | 
|---|
| 296 | } | 
|---|
| 297 |  | 
|---|
| 298 | /// Returns current stack pointer. | 
|---|
| 299 | /// | 
|---|
| 300 | /// Note, that the stack pointer returned is from the perspective of the caller. From the | 
|---|
| 301 | /// perspective of `stack_pointer` function the pointer returned is the frame pointer. | 
|---|
| 302 | /// | 
|---|
| 303 | /// While it is a goal to minimize the amount of stack used by this function, implementations for | 
|---|
| 304 | /// some targets may be unable to avoid allocating a stack frame. This makes this function | 
|---|
| 305 | /// suitable for stack exhaustion detection only in conjunction with sufficient padding. | 
|---|
| 306 | /// | 
|---|
| 307 | /// Using `stack_pointer` to check for stack exhaustion is tricky to get right. It is impossible to | 
|---|
| 308 | /// know the callee’s frame size, therefore such value must be derived some other way. A common | 
|---|
| 309 | /// approach is to use stack padding (reserve enough stack space for any function to be called) and | 
|---|
| 310 | /// check against the padded threshold. If padding is chosen incorrectly, a situation similar to | 
|---|
| 311 | /// one described below may occur: | 
|---|
| 312 | /// | 
|---|
| 313 | /// 1. For stack exhaustion check, remaining stack is checked against `stack_pointer` with the | 
|---|
| 314 | ///    padding applied; | 
|---|
| 315 | /// 2. Callee allocates more stack than was accounted for with padding, and accesses pages outside | 
|---|
| 316 | ///    the stack, invalidating the execution (by e.g. crashing). | 
|---|
| 317 | #[ cfg(asm)] | 
|---|
| 318 | pub fn stack_pointer() -> *mut u8 { | 
|---|
| 319 | unsafe { rust_psm_stack_pointer() } | 
|---|
| 320 | } | 
|---|
| 321 |  | 
|---|
| 322 | /// Macro that outputs its tokens only if `psm::on_stack` and `psm::replace_stack` are available. | 
|---|
| 323 | /// | 
|---|
| 324 | /// # Examples | 
|---|
| 325 | /// | 
|---|
| 326 | /// ``` | 
|---|
| 327 | /// # use psm::psm_stack_manipulation; | 
|---|
| 328 | /// psm_stack_manipulation! { | 
|---|
| 329 | ///     yes { | 
|---|
| 330 | ///         /* Functions `on_stack` and `replace_stack` are available here */ | 
|---|
| 331 | ///     } | 
|---|
| 332 | ///     no { | 
|---|
| 333 | ///         /* Functions `on_stack` and `replace_stack` are not available here */ | 
|---|
| 334 | ///     } | 
|---|
| 335 | /// } | 
|---|
| 336 | /// ``` | 
|---|
| 337 | #[ cfg(switchable_stack)] | 
|---|
| 338 | #[ macro_export] | 
|---|
| 339 | macro_rules! psm_stack_manipulation { | 
|---|
| 340 | (yes { $($yes: tt)* } no { $($no: tt)* }) => { $($yes)* }; | 
|---|
| 341 | } | 
|---|
| 342 |  | 
|---|
| 343 | /// Macro that outputs its tokens only if `psm::on_stack` and `psm::replace_stack` are available. | 
|---|
| 344 | /// | 
|---|
| 345 | /// # Examples | 
|---|
| 346 | /// | 
|---|
| 347 | /// ``` | 
|---|
| 348 | /// # use psm::psm_stack_manipulation; | 
|---|
| 349 | /// psm_stack_manipulation! { | 
|---|
| 350 | ///     yes { | 
|---|
| 351 | ///         /* Functions `on_stack` and `replace_stack` are available here */ | 
|---|
| 352 | ///     } | 
|---|
| 353 | ///     no { | 
|---|
| 354 | ///         /* Functions `on_stack` and `replace_stack` are not available here */ | 
|---|
| 355 | ///     } | 
|---|
| 356 | /// } | 
|---|
| 357 | /// ``` | 
|---|
| 358 | #[ cfg(not(switchable_stack))] | 
|---|
| 359 | #[ macro_export] | 
|---|
| 360 | macro_rules! psm_stack_manipulation { | 
|---|
| 361 | (yes { $($yes: tt)* } no { $($no: tt)* }) => { $($no)* }; | 
|---|
| 362 | } | 
|---|
| 363 |  | 
|---|
| 364 | /// Macro that outputs its tokens only if `psm::stack_pointer` and `psm::StackDirection::new` are | 
|---|
| 365 | /// available. | 
|---|
| 366 | /// | 
|---|
| 367 | /// # Examples | 
|---|
| 368 | /// | 
|---|
| 369 | /// ``` | 
|---|
| 370 | /// # use psm::psm_stack_information; | 
|---|
| 371 | /// psm_stack_information! { | 
|---|
| 372 | ///     yes { | 
|---|
| 373 | ///         /* `psm::stack_pointer` and `psm::StackDirection::new` are available here */ | 
|---|
| 374 | ///     } | 
|---|
| 375 | ///     no { | 
|---|
| 376 | ///         /* `psm::stack_pointer` and `psm::StackDirection::new` are not available here */ | 
|---|
| 377 | ///     } | 
|---|
| 378 | /// } | 
|---|
| 379 | /// ``` | 
|---|
| 380 | #[ cfg(asm)] | 
|---|
| 381 | #[ macro_export] | 
|---|
| 382 | macro_rules! psm_stack_information { | 
|---|
| 383 | (yes { $($yes: tt)* } no { $($no: tt)* }) => { $($yes)* }; | 
|---|
| 384 | } | 
|---|
| 385 |  | 
|---|
| 386 | /// Macro that outputs its tokens only if `psm::stack_pointer` and `psm::StackDirection::new` are | 
|---|
| 387 | /// available. | 
|---|
| 388 | /// | 
|---|
| 389 | /// # Examples | 
|---|
| 390 | /// | 
|---|
| 391 | /// ``` | 
|---|
| 392 | /// # use psm::psm_stack_information; | 
|---|
| 393 | /// psm_stack_information! { | 
|---|
| 394 | ///     yes { | 
|---|
| 395 | ///         /* `psm::stack_pointer` and `psm::StackDirection::new` are available here */ | 
|---|
| 396 | ///     } | 
|---|
| 397 | ///     no { | 
|---|
| 398 | ///         /* `psm::stack_pointer` and `psm::StackDirection::new` are not available here */ | 
|---|
| 399 | ///     } | 
|---|
| 400 | /// } | 
|---|
| 401 | /// ``` | 
|---|
| 402 | #[ cfg(not(asm))] | 
|---|
| 403 | #[ macro_export] | 
|---|
| 404 | macro_rules! psm_stack_information { | 
|---|
| 405 | (yes { $($yes: tt)* } no { $($no: tt)* }) => { $($no)* }; | 
|---|
| 406 | } | 
|---|
| 407 |  | 
|---|