zephyr/object.rs
1//! # Zephyr Kernel Objects
2//!
3//! Zephyr has a concept of a 'kernel object' that is handled a bit magically. In kernel mode
4//! threads, these are just pointers to the data structures that Zephyr uses to manage that item.
5//! In userspace, they are still pointers, but those data structures aren't accessible to the
6//! thread. When making syscalls, the kernel validates that the objects are both valid kernel
7//! objects and that the are supposed to be accessible to this thread.
8//!
9//! In many Zephyr apps, the kernel objects in the app are defined as static, using special macros.
10//! These macros make sure that the objects get registered so that they are accessible to userspace
11//! (at least after that access is granted).
12//!
13//! There are also kernel objects that are synthesized as part of the build. Most notably, there
14//! are ones generated by the device tree.
15//!
16//! ## Safety
17//!
18//! Zephyr has traditionally not focused on safety. Early versions of project goals, in fact,
19//! emphasized performance and small code size as priorities over runtime checking of safety. Over
20//! the years, this focus has changed a bit, and Zephyr does contain some additional checking, some
21//! of which is optional.
22//!
23//! Zephyr is still constrained at compile time to checks that can be performed with the limits
24//! of the C language. With Rust, we have a much greater ability to enforce many aspects of safety
25//! at compile time. However, there is some complexity to doing this at the interface between the C
26//! world and Rust.
27//!
28//! There are two types of kernel objects we deal with. There are kernel objects that are allocated
29//! by C code (often auto-generated) that should be accessible to Rust. These are mostly `struct
30//! device` values, and will be handled in a devices module. The other type are objects that
31//! application code wishes to declare statically, and use from Rust code. That is the
32//! responsibility of this module. (There will also be support for more dynamic management of
33//! kernel objects, but this will be handled later).
34//!
35//! Static kernel objects in Zephyr are declared as C top-level variables (where the keyword static
36//! means something different). It is the responsibility of the calling code to initialize these
37//! items, make sure they are only initialized once, and to ensure that sharing of the object is
38//! handled properly. All of these are concerns we can handle in Rust.
39//!
40//! To handle initialization, we pair each kernel object with a single atomic value, whose zero
41//! value indicates [`KOBJ_UNINITIALIZED`]. There are a few instances of values that can be placed
42//! into uninitialized memory in a C declaration that will need to be zero initialized as a Rust
43//! static. The case of thread stacks is handled as a special case, where the initialization
44//! tracking is kept separate so that the stack can still be placed in initialized memory.
45//!
46//! This state goes through two more values as the item is initialized, one indicating the
47//! initialization is happening, and another indicating it has finished.
48//!
49//! For each kernel object, there will be two types. One, having a name of the form StaticThing,
50//! and the other having the form Thing. The StaticThing will be used in a static declaration.
51//! There is a [`kobj_define!`] macro that matches declarations of these values and adds the
52//! necessary linker declarations to place these in the correct linker sections. This is the
53//! equivalent of the set of macros in C, such as `K_SEM_DEFINE`.
54//!
55//! This StaticThing will have a single method [`init_once`] which accepts a single argument of a
56//! type defined by the object. For most objects, it will just be an empty tuple `()`, but it can
57//! be whatever initializer is needed for that type by Zephyr. Semaphores, for example, take the
58//! initial value and the limit. Threads take as an initializer the stack to be used.
59//!
60//! This `init_once` will initialize the Zephyr object and return the `Thing` item that will have
61//! the methods on it to use the object. Attributes such as `Sync`, and `Clone` will be defined
62//! appropriately so as to match the semantics of the underlying Zephyr kernel object. Generally
63//! this `Thing` type will simply be a container for a direct pointer, and thus using and storing
64//! these will have the same characteristics as it would from C.
65//!
66//! Rust has numerous strict rules about mutable references, namely that it is not safe to have more
67//! than one mutable reference. The language does allow multiple `*mut ktype` references, and their
68//! safety depends on the semantics of what is pointed to. In the case of Zephyr, some of these are
69//! intentionally thread safe (for example, things like `k_sem` which have the purpose of
70//! synchronizing between threads). Others are not, and that is mirrored in Rust by whether or not
71//! `Clone` and/or `Sync` are implemented. Please see the documentation of individual entities for
72//! details for that object.
73//!
74//! In general, methods on `Thing` will require `&mut self` if there is any state to manage. Those
75//! that are built around synchronization primitives, however, will generally use `&self`. In
76//! general, objects that implement `Clone` will use `&self` because there would be no benefit to
77//! mutable self when the object could be cloned.
78//!
79//! [`kobj_define!`]: crate::kobj_define
80//! [`init_once`]: StaticKernelObject::init_once
81
82#[cfg(CONFIG_RUST_ALLOC)]
83extern crate alloc;
84
85use core::{cell::UnsafeCell, mem};
86
87#[cfg(CONFIG_RUST_ALLOC)]
88use core::pin::Pin;
89
90#[cfg(CONFIG_RUST_ALLOC)]
91use alloc::boxed::Box;
92
93use crate::sync::atomic::{AtomicUsize, Ordering};
94
95/// ## Init/move safe objects
96///
97/// In Rust code, many language features are designed around Rust's "move semantics". Because of
98/// the borrow checker, the Rust compiler has full knowledge of when it is safe to move an object in
99/// memory, as it will know that there are no references to it.
100///
101/// However, most kernel objects in Zephyr contain self-referential pointers to those objects. The
102/// traditional way to handle this in Rust is to use `Pin`. However, besides Pin being awkward to
103/// use, it is generally assumed that the underlying objects will be dynamically allocated. It is
104/// desirable to allow as much functionality of Zephyr to be used without explicitly requiring
105/// alloc.
106///
107/// The original solution (Wrapped), used a type `Fixed` that either referenced a static, or
108/// contained a `Pin<Box<T>>` of the Zephyr kernel object. This introduces overhead for both the
109/// enum as well as the actual reference itself.
110///
111/// Some simple benchmarking has determined that it is just as efficient, or even slightly more so,
112/// to represent each object instead as a `UnsafeCell` contaning the Zephyr object, and an atomic
113/// pointer that can be used to determine the state of the object.
114///
115/// This type is not intended for general use, but for the implementation of wrappers for Zephyr
116/// types that require non-move semantics.
117///
118/// # Safety
119///
120/// The Zephyr APIs require that once objects have been initialized, they are not moved in memory.
121/// To avoid the inconvenience of managing 'Pin' for most of these, we rely on a run-time detection
122/// both of initialization and non-movement. It is fairly easy, as a user of an object in Rust to
123/// avoid moving it. Generally, in an embedded system, objects will either live on the stack of a
124/// single persistent thread, or will be statically allocated. Both of these cases will result in
125/// objects that don't move. However, we want initialization to be able to happen on first _use_
126/// not when the constructor runs, because the semantics of most constructors invovles a move (even
127/// if that is often optimized away).
128///
129/// Note that this does not solve the case of objects that must not be moved even after the object
130/// has a single Rust reference (threads, and work queues, notably, or timers with active
131/// callbacks).
132///
133/// To do this, each object is paired with an Atomic pointer. The pointer can exist in three state:
134/// - null: The object has not been initialized. It is safe to move the object at this time.
135/// - pointer that equals the addres of the object itself. Object has been initialized, and can be
136/// used. It must not be moved.
137/// - pointer that doesn't match the object. This indicates that the object was moved, and is
138/// invalid. We treat this as a panic condition.
139pub struct ZephyrObject<T> {
140 state: AtomicUsize,
141 object: UnsafeCell<T>,
142}
143
144impl<T> ZephyrObject<T>
145where
146 ZephyrObject<T>: ObjectInit<T>,
147{
148 /// Construct a new Zephyr Object.
149 ///
150 /// The 'init' function will be given a reference to the object. For objects that have
151 /// initialization parameters (specifically Semaphores), this can be used to hold those
152 /// parameters until the real init is called upon first use.
153 ///
154 /// The 'setup' function must not assume the address given to it will persist. The object can
155 /// be freely moved by Rust until the 'init' call has been called, which happens on first use.
156 pub const fn new_raw() -> Self {
157 Self {
158 state: AtomicUsize::new(0),
159 // SAFETY: It is safe to assume Zephyr objects can be zero initialized before calling
160 // their init. The atomic above will ensure that this is not used by any API other than
161 // the init call until it has been initialized.
162 object: UnsafeCell::new(unsafe { mem::zeroed() }),
163 }
164 }
165
166 /// Get a reference, _without initializing_ the item.
167 ///
168 /// This is useful during a const constructor to be able to stash values in the item.
169 pub const fn get_uninit(&self) -> *mut T {
170 self.object.get()
171 }
172
173 /// Get a reference to the underlying zephyr object, ensuring it has been initialized properly.
174 /// The method is unsafe, because the caller must ensure that the lifetime of `&self` is long
175 /// enough for the use of the raw pointer.
176 ///
177 /// # Safety
178 ///
179 /// If the object has not been initialized, It's 'init' method will be called. If the object
180 /// has been moved since `init` was called, this will panic. Otherwise, the caller must ensure
181 /// that the use of the returned pointer does not outlive the `&self`.
182 ///
183 /// The 'init' method will be called within a critical section, so should be careful to not
184 /// block, or take extra time.
185 pub unsafe fn get(&self) -> *mut T {
186 let addr = self.object.get();
187
188 // First, try reading the atomic relaxed. This makes the common case of the object being
189 // initialized faster, and we can double check after.
190 match self.state.load(Ordering::Relaxed) {
191 // Uninitialized. Falls through to the slower init case.
192 0 => (),
193 // Initialized, and object has not been moved.
194 ptr if ptr == addr as usize => return addr,
195 _ => {
196 // Object was moved after init.
197 panic!("Object moved after init");
198 }
199 }
200
201 // Perform the possible initialization within a critical section to avoid a race and double
202 // initialization.
203 critical_section::with(|_| {
204 // Reload, with Acquire ordering to see a determined value.
205 let state = self.state.load(Ordering::Acquire);
206
207 // If the address does match, an initialization got in before the critical section.
208 if state == addr as usize {
209 // Note, this returns from the closure, not the function, but this is ok, as the
210 // critical section result is the return result of the whole function.
211 return addr;
212 } else if state != 0 {
213 // Initialization got in, and then it was moved. This shouldn't happen without
214 // unsafe code, but it is easy to detect.
215 panic!("Object moved after init");
216 }
217
218 // Perform the initialization.
219 <Self as ObjectInit<T>>::init(addr);
220
221 self.state.store(addr as usize, Ordering::Release);
222
223 addr
224 })
225 }
226}
227
228/// All `ZephyrObject`s must implement `ObjectInit` in order for first use to be able to initialize
229/// the object.
230pub trait ObjectInit<T> {
231 /// Initialize the object.
232 ///
233 /// This is called upon first use. The address given may (and generally will) be different than
234 /// the initial address given to the `setup` call in the [`ZephyrObject::new_raw`] constructor.
235 /// After this is called, all subsequent calls to [`ZephyrObject::get`] will return the same
236 /// address, or panic.
237 fn init(item: *mut T);
238}
239
240// The kernel object itself must be wrapped in `UnsafeCell` in Rust. This does several thing, but
241// the primary feature that we want to declare to the Rust compiler is that this item has "interior
242// mutability". One impact will be that the default linker section will be writable, even though
243// the object will not be declared as mutable. It also affects the compiler as it will avoid things
244// like aliasing and such on the data, as it will know that it is potentially mutable. In our case,
245// the mutations happen from C code, so this is less important than the data being placed in the
246// proper section. Many will have the link section overridden by the `kobj_define` macro.
247
248/// ## Old Wrapped objects
249///
250/// The wrapped objects was the original approach to managing Zephyr objects.
251///
252/// Define the Wrapping of a kernel object.
253///
254/// This trait defines the association between a static kernel object and the two associated Rust
255/// types: `StaticThing` and `Thing`. In the general case: there should be:
256/// ```
257/// impl Wrapped for StaticKernelObject<kobj> {
258/// type T = Thing,
259/// type I = (),
260/// fn get_wrapped(&self, args: Self::I) -> Self::T {
261/// let ptr = self.value.get();
262/// // Initizlie the kobj using ptr and possible the args.
263/// Thing { ptr }
264/// }
265/// }
266/// ```
267pub trait Wrapped {
268 /// The wrapped type. This is what `init_once()` on the StaticKernelObject will return after
269 /// initialization.
270 type T;
271
272 /// The wrapped type also requires zero or more initializers. Which are represented by this
273 /// type.
274 type I;
275
276 /// Initialize this kernel object, and return the wrapped pointer.
277 fn get_wrapped(&self, args: Self::I) -> Self::T;
278}
279
280/// A state indicating an uninitialized kernel object.
281///
282/// This must be zero, as kernel objects will
283/// be represetned as zero-initialized memory.
284pub const KOBJ_UNINITIALIZED: usize = 0;
285
286/// A state indicating a kernel object that is being initialized.
287pub const KOBJ_INITING: usize = 1;
288
289/// A state indicating a kernel object that has completed initialization. This also means that the
290/// take has been called. And shouldn't be allowed additional times.
291pub const KOBJ_INITIALIZED: usize = 2;
292
293/// A kernel object represented statically in Rust code.
294///
295/// These should not be declared directly by the user, as they generally need linker decorations to
296/// be properly registered in Zephyr as kernel objects. The object has the underlying Zephyr type
297/// T, and the wrapper type W.
298///
299/// Kernel objects will have their `StaticThing` implemented as `StaticKernelObject<kobj>` where
300/// `kobj` is the type of the underlying Zephyr object. `Thing` will usually be a struct with a
301/// single field, which is a `*mut kobj`.
302///
303/// TODO: Can we avoid the public fields with a const new method?
304///
305/// TODO: Handling const-defined alignment for these.
306pub struct StaticKernelObject<T> {
307 #[allow(dead_code)]
308 /// The underlying zephyr kernel object.
309 pub value: UnsafeCell<T>,
310 /// Initialization status of this object. Most objects will start uninitialized and be
311 /// initialized manually.
312 pub init: AtomicUsize,
313}
314
315impl<T> StaticKernelObject<T>
316where
317 StaticKernelObject<T>: Wrapped,
318{
319 /// Construct an empty of these objects, with the zephyr data zero-filled.
320 ///
321 /// # Safety
322 ///
323 /// This is safe in the sense that Zephyr we track the initialization, they start in the
324 /// uninitialized state, and the zero value of the initialize atomic indicates that it is
325 /// uninitialized.
326 pub const unsafe fn new() -> StaticKernelObject<T> {
327 StaticKernelObject {
328 value: unsafe { mem::zeroed() },
329 init: AtomicUsize::new(KOBJ_UNINITIALIZED),
330 }
331 }
332
333 /// Get the instance of the kernel object.
334 ///
335 /// Will return a single wrapped instance of this object. This will invoke the initialization,
336 /// and return `Some<Wrapped>` for the wrapped containment type.
337 ///
338 /// If it is called an additional time, it will return None.
339 pub fn init_once(&self, args: <Self as Wrapped>::I) -> Option<<Self as Wrapped>::T> {
340 if self
341 .init
342 .compare_exchange(
343 KOBJ_UNINITIALIZED,
344 KOBJ_INITING,
345 Ordering::AcqRel,
346 Ordering::Acquire,
347 )
348 .is_err()
349 {
350 return None;
351 }
352 let result = self.get_wrapped(args);
353 self.init.store(KOBJ_INITIALIZED, Ordering::Release);
354 Some(result)
355 }
356}
357
358/// Objects that can be fixed or allocated.
359///
360/// When using Rust threads from userspace, the `kobj_define` declarations and the complexity behind
361/// it is required. If all Rust use of kernel objects is from system threads, and dynamic memory is
362/// available, kernel objects can be freeallocated, as long as the allocations themselves are
363/// pinned. This `Fixed` encapsulates both of these.
364pub enum Fixed<T> {
365 /// Objects that have been statically declared and just pointed to.
366 Static(*mut T),
367 /// Objects that are owned by the wrapper, and contained here.
368 #[cfg(CONFIG_RUST_ALLOC)]
369 Owned(Pin<Box<UnsafeCell<T>>>),
370}
371
372impl<T> Fixed<T> {
373 /// Get the raw pointer out of the fixed object.
374 ///
375 /// Returns the `*mut T` pointer held by this object. It is either just the static pointer, or
376 /// the pointer outside of the unsafe cell holding the dynamic kernel object.
377 pub fn get(&self) -> *mut T {
378 match self {
379 Fixed::Static(ptr) => *ptr,
380 #[cfg(CONFIG_RUST_ALLOC)]
381 Fixed::Owned(item) => item.get(),
382 }
383 }
384
385 /// Construct a new fixed from an allocation. Note that the object will not be fixed in memory,
386 /// until _after_ this returns, and it should not be initialized until then.
387 #[cfg(CONFIG_RUST_ALLOC)]
388 pub fn new(item: T) -> Fixed<T> {
389 Fixed::Owned(Box::pin(UnsafeCell::new(item)))
390 }
391}
392
393/// Declare a static kernel object. This helps declaring static values of Zephyr objects.
394///
395/// This can typically be used as:
396/// ```
397/// kobj_define! {
398/// static A_MUTEX: StaticMutex;
399/// static MUTEX_ARRAY: [StaticMutex; 4];
400/// }
401/// ```
402#[macro_export]
403macro_rules! kobj_define {
404 ($v:vis static $name:ident: $type:tt; $($rest:tt)*) => {
405 $crate::_kobj_rule!($v, $name, $type);
406 $crate::kobj_define!($($rest)*);
407 };
408 ($v:vis static $name:ident: $type:tt<$size:ident>; $($rest:tt)*) => {
409 $crate::_kobj_rule!($v, $name, $type<$size>);
410 $crate::kobj_define!($($rest)*);
411 };
412 ($v:vis static $name:ident: $type:tt<$size:literal>; $($rest:tt)*) => {
413 $crate::_kobj_rule!($v, $name, $type<$size>);
414 $crate::kobj_define!($($rest)*);
415 };
416 ($v:vis static $name:ident: $type:tt<{$size:expr}>; $($rest:tt)*) => {
417 $crate::_kobj_rule!($v, $name, $type<{$size}>);
418 $crate::kobj_define!($($rest)*);
419 };
420 () => {};
421}
422
423#[doc(hidden)]
424#[macro_export]
425macro_rules! _kobj_rule {
426 // static NAME: StaticSemaphore;
427 ($v:vis, $name:ident, StaticSemaphore) => {
428 #[link_section = concat!("._k_sem.static.", stringify!($name), ".", file!(), line!())]
429 $v static $name: $crate::sys::sync::StaticSemaphore =
430 unsafe { ::core::mem::zeroed() };
431 };
432
433 // static NAMES: [StaticSemaphore; COUNT];
434 ($v:vis, $name:ident, [StaticSemaphore; $size:expr]) => {
435 #[link_section = concat!("._k_sem.static.", stringify!($name), ".", file!(), line!())]
436 $v static $name: [$crate::sys::sync::StaticSemaphore; $size] =
437 unsafe { ::core::mem::zeroed() };
438 };
439
440 // static NAME: StaticMutex
441 ($v:vis, $name:ident, StaticMutex) => {
442 #[link_section = concat!("._k_mutex.static.", stringify!($name), ".", file!(), line!())]
443 $v static $name: $crate::sys::sync::StaticMutex =
444 unsafe { $crate::sys::sync::StaticMutex::new() };
445 };
446
447 // static NAMES: [StaticMutex; COUNT];
448 ($v:vis, $name:ident, [StaticMutex; $size:expr]) => {
449 #[link_section = concat!("._k_mutex.static.", stringify!($name), ".", file!(), line!())]
450 $v static $name: [$crate::sys::sync::StaticMutex; $size] =
451 // This isn't Copy, intentionally, so initialize the whole thing with zerored memory.
452 // Relying on the atomic to be 0 for the uninitialized state.
453 // [$crate::sys::sync::StaticMutex::new(); $size];
454 unsafe { ::core::mem::zeroed() };
455 };
456
457 // static NAME: StaticCondvar;
458 ($v:vis, $name:ident, StaticCondvar) => {
459 #[link_section = concat!("._k_condvar.static.", stringify!($name), ".", file!(), line!())]
460 $v static $name: $crate::sys::sync::StaticCondvar =
461 unsafe { $crate::sys::sync::StaticCondvar::new() };
462 };
463
464 // static NAMES: [StaticCondvar; COUNT];
465 ($v:vis, $name:ident, [StaticCondvar; $size:expr]) => {
466 #[link_section = concat!("._k_condvar.static.", stringify!($name), ".", file!(), line!())]
467 $v static $name: [$crate::sys::sync::StaticCondvar; $size] =
468 // This isn't Copy, intentionally, so initialize the whole thing with zerored memory.
469 // Relying on the atomic to be 0 for the uninitialized state.
470 // [$crate::sys::sync::StaticMutex::new(); $size];
471 unsafe { ::core::mem::zeroed() };
472 };
473
474 // static THREAD: staticThread;
475 ($v:vis, $name:ident, StaticThread) => {
476 // Since the static object has an atomic that we assume is initialized, we cannot use the
477 // default linker section Zephyr uses for Thread, as that is uninitialized. This will put
478 // it in .bss, where it is zero initialized.
479 $v static $name: $crate::sys::thread::StaticThread =
480 unsafe { ::core::mem::zeroed() };
481 };
482
483 // static THREAD: [staticThread; COUNT];
484 ($v:vis, $name:ident, [StaticThread; $size:expr]) => {
485 // Since the static object has an atomic that we assume is initialized, we cannot use the
486 // default linker section Zephyr uses for Thread, as that is uninitialized. This will put
487 // it in .bss, where it is zero initialized.
488 $v static $name: [$crate::sys::thread::StaticThread; $size] =
489 unsafe { ::core::mem::zeroed() };
490 };
491
492 // Use indirection on stack initializers to handle some different cases in the Rust syntax.
493 ($v:vis, $name:ident, ThreadStack<$size:literal>) => {
494 $crate::_kobj_stack!($v, $name, $size);
495 };
496 ($v:vis, $name:ident, ThreadStack<$size:ident>) => {
497 $crate::_kobj_stack!($v, $name, $size);
498 };
499 ($v:vis, $name:ident, ThreadStack<{$size:expr}>) => {
500 $crate::_kobj_stack!($v, $name, $size);
501 };
502
503 // Array of stack object versions.
504 ($v:vis, $name:ident, [ThreadStack<$size:literal>; $asize:expr]) => {
505 $crate::_kobj_stack!($v, $name, $size, $asize);
506 };
507 ($v:vis, $name:ident, [ThreadStack<$size:ident>; $asize:expr]) => {
508 $crate::_kobj_stack!($v, $name, $size, $asize);
509 };
510 ($v:vis, $name:ident, [ThreadStack<{$size:expr}>; $asize:expr]) => {
511 $crate::_kobj_stack!($v, $name, $size, $asize);
512 };
513
514 // Queues.
515 ($v:vis, $name: ident, StaticQueue) => {
516 #[link_section = concat!("._k_queue.static.", stringify!($name), ".", file!(), line!())]
517 $v static $name: $crate::sys::queue::StaticQueue =
518 unsafe { ::core::mem::zeroed() };
519 };
520
521 ($v:vis, $name: ident, [StaticQueue; $size:expr]) => {
522 #[link_section = concat!("._k_queue.static.", stringify!($name), ".", file!(), line!())]
523 $v static $name: [$crate::sys::queue::StaticQueue; $size] =
524 unsafe { ::core::mem::zeroed() };
525 };
526}
527
528#[doc(hidden)]
529#[macro_export]
530macro_rules! _kobj_stack {
531 ($v:vis, $name: ident, $size:expr) => {
532 $crate::paste! {
533 // The actual stack itself goes into the no-init linker section. We'll use the user_name,
534 // with _REAL appended, to indicate the real stack.
535 #[link_section = concat!(".noinit.", stringify!($name), ".", file!(), line!())]
536 $v static [< $name _REAL >]: $crate::sys::thread::RealStaticThreadStack<{$crate::sys::thread::stack_len($size)}> =
537 unsafe { ::core::mem::zeroed() };
538
539 // The proxy object used to ensure initialization is placed in initialized memory.
540 $v static $name: $crate::_export::KStaticThreadStack =
541 $crate::_export::KStaticThreadStack::new_from(&[< $name _REAL >]);
542 }
543 };
544
545 // This initializer needs to have the elements of the array initialized to fixed elements of the
546 // `RealStaticThreadStack`. Unfortunately, methods such as [`each_ref`] on the array are not
547 // const and can't be used in a static initializer. We could use a recursive macro definition
548 // to perform the initialization, but this would require the array size to only be an integer
549 // literal (constants aren't calculated until after macro expansion). It may also be possible
550 // to write a constructor for the array as a const fn, which would greatly simplify the
551 // initialization here.
552 ($v:vis, $name: ident, $size:expr, $asize:expr) => {
553 $crate::paste! {
554 // The actual stack itself goes into the no-init linker section. We'll use the user_name,
555 // with _REAL appended, to indicate the real stack.
556 #[link_section = concat!(".noinit.", stringify!($name), ".", file!(), line!())]
557 $v static [< $name _REAL >]:
558 [$crate::sys::thread::RealStaticThreadStack<{$crate::sys::thread::stack_len($size)}>; $asize] =
559 unsafe { ::core::mem::zeroed() };
560
561 $v static $name:
562 [$crate::_export::KStaticThreadStack; $asize] =
563 $crate::_export::KStaticThreadStack::new_from_array(&[< $name _REAL >]);
564 }
565 };
566}