Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

New static mutable storage for the entry function

Open
#616 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
35/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Stale
Tech stack
rust
Domain
embedded-iot

Research direction

Start by reviewing the existing #[entry] macro behavior and the proposed entry_with_context syntax, then compare the RTIC local-resource example described in the issue. Trace how the current static mut expansion reaches the entry function and define the desired context API and expansion; done means the design is agreed and its safety and compatibility implications are documented.

Written by the indexing model from the issue text.

Description

We carefully ensure the #[entry] function cannot be called other than after a reset (and after the start-up code has initialised everything).

Currently we use this knowledge to 'safely' replace static mut FOO: T = T::new() with code that uses an unsafe block to create &mut T reference to the static, which is passed in as an argument.

That is:

#[cortex_m_rt::entry]
fn main() -> ! {
    static mut FOO: u32 = 123;

    loop {}
}

becomes:

#[doc(hidden)]
#[export_name = "main"]
pub unsafe extern "C" fn __cortex_m_rt_main_trampoline() {
    #[allow(static_mut_refs)]
    __cortex_m_rt_main({
        static mut FOO: u32 = 123;
        unsafe { &mut FOO }
    })
}
fn __cortex_m_rt_main(#[allow(non_snake_case)] FOO: &'static mut u32) -> ! {
    loop {}
}

It is widely accepted that this kind of sleight of hand is not good. However, RTIC shows us a syntax that could work:

#[task(local = [state: u32 = 0])]
async fn foo(c: foo::Context) {
    let old_state: u32 = *c.local.state;
    *c.local.state += 1;
}

So, what if we wrote a new macro, called entry_with_context:

#[cortex_m_rt::entry_with_context(context = [state: u32 = 0])]
fn main(c: main::Context) -> ! {
    let old_state: u32 = *c.state;
    *c.state += 1;

    loop {}
}

The advantage here is that is clear some 'magic' is happening to create a static resource and pass it to the main function.

It would expand to something like:

mod main {
    pub(crate) struct Context {
        state: &mut u32
    }
}

#[doc(hidden)]
#[export_name = "main"]
pub unsafe extern "C" fn __cortex_m_rt_main_trampoline() {
    static STATE: RacyCell<u32> = RacyCell::new(123);
    __cortex_m_rt_main(main::Context {
        state: unsafe { STATE.get_mut_ref() },
    })
}

fn main(c: main::Context) -> ! {
    let old_state: u32 = *c.state;
    *c.state += 1;

    loop {}
}

We don't even need to hide the actual fn main() function anymore, because you cannot call it without creating the context object.

Dominant language
Rust
Stars
1k
Forks
201
Avg merge
1h 8m
Merged PRs (30d)
2

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from rust-embedded/cortex-m

All issues in rust-embedded/cortex-m

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.