What exactly is "memory accessible outside of the assembly code" / The semantics of `nomem`/`readonly`?

⚓ Rust    📅 2025-10-03    👤 surdeus    👁️ 5      

surdeus

I'm writing assembly code on no_std Linux and have found myself confused on the semantics and examples of the nomem and readonly inline assembly options

For example nomem says "The assembly code does not read from or write to any memory accessible outside of the assembly code" and provides as an example

let mut x = 0i32;
let z: i32;
// Accessing outside memory from assembly when `nomem` is
// specified is disallowed
unsafe {
    core::arch::asm!("mov {val:e}, dword ptr [{ptr}]",
        ptr = in(reg) &mut x,
        val = lateout(reg) z,
        options(nomem)
    )
}

But to me this should be allowed because x is stack local, it is not global, it is mut, its pointer is passed directly to the assembly, and it contains no further pointers, making it not memory "accessible outside of the assembly code" because there is no way it can soundly be accessed from another thread or signal handler or anywhere else.

It also raises the question of why this combination isnt simply a compile error, because if pointers are never usable with nomem, why compile it at all?

There is some elaboration here where it is simply stated "no reads or writes to memory are allowed", but that "These rules do not apply to memory which is private to the assembly code, such as stack space allocated within it". x is clearly not private to the assembly block, so under this wording its clearly not allowed.

However nomem also says it allows "the compiler to cache the values of modified global variables in registers", which suggests that the compiler can't do this optimization without nomem, but that is something I want to allow and see no reason to prevent given the example assembly code, which will never access any memory not directly passed to it even if it would otherwise have been allowed, such as a global. However it seems this is impossible to allow/specify to the compiler?

Similar questions for readonly.

If the assembly code calls another function which then performs a read/write, either of a global or a passed in pointer, is it also bound by nomem or readonly? The assembly code itself isnt? reading or writing any passed memory in that scenario, and all of the rules specifically only talk about "the assembly code". Does whether its UB then depend on what the function being called does?

Does call itself count as a memory read? jmp emulating call? What if call/jmp RIP relative rather than a memory address? Are functions globals?

1 post - 1 participant

Read full topic

🏷️ Rust_feed