Skip to content

Add support for naked functions. #1201

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 21, 2016
Merged

Conversation

tari
Copy link
Contributor

@tari tari commented Jul 11, 2015

Rendered.

[edited to link to the final rendered version]

@eddyb
Copy link
Member

eddyb commented Jul 11, 2015

👍 This would allow using only Rust + inline assembly in a kernel.
Definitely need to restrict this to either unsafe functions, or have unsafe in the name of the attribute (I prefer the former).

@nrc nrc added the T-lang Relevant to the language team, which will review and decide on the RFC. label Jul 13, 2015
@DanielKeep
Copy link

How does this interact with the calling convention?

I recently looked into writing some inline assembly, using extern "C" so that I knew the layout of the stack, but it looks like in Rust this just creates an extern "C" landing pad that calls an extern "rust" function which is where the code actually lives.

If all this does is let us replace the contents of an extern "rust" function, and the Rust ABI is deliberately undefined, how is one supposed to write code that accesses arguments or returns anything?

I'm inclined to suggest that #[naked] be restricted not only to unsafe functions, but functions that use a "well-defined" ABI such as C or stdcall. Unless the Rust ABI is defined, there's no way #[naked] unsafe extern "rust" fn() { ... } can have any well-defined meaning.

@Tobba
Copy link

Tobba commented Jul 14, 2015

This doesn't seem like a terribly good idea, LLVM lacked this for a very long time due to the undefined and extremely iffy semantics involved, essentially the only thing that can safely be done in a naked function is using a single asm block. LLVM already has had support for module-level assembly blocks as long as I can remember.

Although I wouldn't let any remotely complicated hand-written code get within 50 feet of LLVMs built-in assembler either (it tends to be somewhat broken)

@tari
Copy link
Contributor Author

tari commented Jul 14, 2015

If all this does is let us replace the contents of an extern "rust" function, and the Rust ABI is deliberately undefined, how is one supposed to write code that accesses arguments or returns anything?

I think you're right that anything but parameterless functions returning () are difficult or impossible to write as naked. Interrupt handlers always match those criteria, but that seems like a good case in support of the extern "interrupt" alternative since doing anything else with a naked function is so sketchy.

LLVM already has had support for module-level assembly blocks as long as I can remember.

Without Rust support though, that doesn't get us very far. The advantage of putting this directly on functions is that we allow the compiler to generate a function definition, including name mangling.


Despite the much greater implementation complexity, I'm coming around to the extern "interrupt" option as more robust and safer than this one, especially if nobody can come up with a non-interrupt use case for naked functions.

@DanielKeep
Copy link

@tari: My point wasn't "you can't write naked functions that take arguments", it was "you can't write naked functions that take arguments using the undefined rust ABI". I've only written a few naked functions over the years, but every time I did, they had to be naked, and they had to take arguments. I think they were all trampolines of one kind or another.

I think the good solution is to make sure that naked functions aren't "desugared" into extern "rust" functions wrapped in an extern "$OTHER" trampoline. After all, I would only expect them to contain a single asm! directive, anyway, so the compiler shouldn't need to translate the ABI.

extern "interrupt", on the other hand, feels more like a "this is the only use case I care about" solution. :P

@Tobba
Copy link

Tobba commented Jul 14, 2015

extern "interrupt" gives the impression that the ABI is only for interrupt handlers, while that is not the case. How about extern "naked"?

Either way, the compiler could provide name mangling for module-level assembly blocks, which would be much saner.

@Diggsey
Copy link
Contributor

Diggsey commented Jul 14, 2015

Isn't name mangling seperate from the ABI already in rust, ie. extern "C" doesn't disable name mangling?

One problem with extern "interrupt"/extern "naked" is that it doesn't specify how the function should be called. For interrupts it's not a problem because you never call them directly, but there may be cases where you need to call a naked function directly from other rust code - in that case the compiler needs to know which ABI to use to call the function.

For this reason, I think it would be better to leave #[naked] as an attribute, but disallow its use in conjunction with extern "rust" functions.

Finally, there may be cases where the naked function doesn't follow any standard ABI, in which case it shouldn't be callable at all from rust code. This "unspecified" ABI could be selected via extern "unspecified", in which case the compiler would prevent it being called except via assembly.

@tari
Copy link
Contributor Author

tari commented Jul 14, 2015

I think the good solution is to make sure that naked functions aren't "desugared" into extern "rust" functions wrapped in an extern "$OTHER" trampoline. After all, I would only expect them to contain a single asm! directive, anyway, so the compiler shouldn't need to translate the ABI.

The trouble with that approach I see is that it becomes impossible to safely call such a naked function from Rust. We could choose to forbid native rust calls to such a function, but that seems to me like an unusual exception to the rule that all declared functions are callable.

extern "interrupt" gives the impression that the ABI is only for interrupt handlers, while that is not the case. How about extern "naked"?

Right; I started conflating the goal and means a bit here. Clarifying: my ultimate goal is to support writing interrupt handlers in Rust-only. The proposed means to that is introducing naked functions which allow the programmer to use an arbitrary calling convention.

As extern functions are implemented today, we cannot implement arbitrary ABI trampolines because the Rust ABI is unspecified and extern functions are implemented by the compiler as a trampoline to translate the calling convention to Rust and call the Rust-native version of a function. This is not a concern for interrupt handlers because they don't take any parameters, nor do they return any values.

Changing this approach would have the implication that some calls to functions defined in Rust become wrong because they assume a different calling convention- a hypothetical extern "naked" function implemented as a module-level assembly block would be unsafe to call from Rust because the ABIs will conflict and it is not possible for the compiler to generate a Rust-ABI version of the function.


Given the previous points, the simplest approach to supporting arbitrary ABIs appears to be implementing them in the compiler. This is much less flexible than supporting naked functions, but permits more efficient code generation since the compiler has detailed knowledge of what the system is doing. For the motivating use case this could be extern "interrupt" to use a target-specific interrupt ABI, or something like extern "fortran" for Fortran FFI.

Addition of calling conventions doesn't preclude a generic mechanism for naked functions, but it does sidestep the current issues involved in making naked functions useful.

@DanielKeep
Copy link

The trouble with that approach I see is that it becomes impossible to safely call such a naked function from Rust. We could choose to forbid native rust calls to such a function, but that seems to me like an unusual exception to the rule that all declared functions are callable.

A naked function is no less safe to call than any other non-local extern "C" function. Heck, that C ABI function you're linking to might very well be implemented in pure assembly for all you know!

I get the feeling that we're starting to talk across purposes, here, so let's clarify. I'm talking about something like:

#[cfg(target_arch="x86")]
extern "C" fn add_two(a: i32, b: i32) -> i32 {
    #![naked]
    asm!(r#"
        mov eax, [esp+4]
        mov ecx, [esp+8]
        add eax, ecx
    "#)
}

fn main() {
    assert_eq!(add_two(4, 6), 10);
}

Unless I've got the assembler wrong (it's been a while), the above should be perfectly safe to call. It's a function that uses the C ABI... that it's written in assembler and doesn't have the standard function prelude is just an implementation detail the user doesn't need to worry about.

I mean, rustc has to be able to call extern "C" functions; it's just that the code it generates when it's defining them is a bit unexpected.

@tari
Copy link
Contributor Author

tari commented Jul 14, 2015

A naked function is no less safe to call than any other non-local extern "C" function. Heck, that C ABI function you're linking to might very well be implemented in pure assembly for all you know!

The difference is that the only reason to use a naked function is when the compiler does not understand the ABI you need to use and is thus virtually guaranteed to generate code that will do something unexpected (and wrong) if you try to call it. You can implement a naked function that uses the C ABI, but there's no reason to do so because the compiler already understands how to deal with that for you.

@Diggsey
Copy link
Contributor

Diggsey commented Jul 14, 2015

@tari That's simply not true - there are many reasons to require a naked function even when using an ABI that the compiler understands.

@tari
Copy link
Contributor Author

tari commented Jul 14, 2015

Can you provide some examples of situations you'd want to do so? The way I see it, naked functions are useful to fill holes in the compiler's ABI support and I don't see the point of using one when the compiler is capable of doing what you need.

@Diggsey
Copy link
Contributor

Diggsey commented Jul 14, 2015

Sometimes you can implement a method more efficiently than the compiler's default prolog/epilog would allow. For example, I needed to write a function which returned the address of the caller. With a naked function, this boils down to two instructions:

mov eax, [ebp+4]
ret

There's just no way to generate code like that without getting rid of the default prolog and epilog.

@main--
Copy link

main-- commented Jul 14, 2015

@Diggsey While specific stack layout (e.g. caller address being at [ebp+4]) is undefined in the rust ABI, prolog/epilog optimization is generally the compiler's job. So if we just ignore the fact that the result of this function is essentially undefined, you can do this:

#![feature(asm)]

pub fn myfunc() -> u32 {
    let ret: u32;
    unsafe { asm!("mov $0, [ebp+4]" : "=r"(ret) ::: "intel"); }
    return ret;
}
// yes, I'm compiling this on x86_64, so it won't ever work like this anyways but it really doesn't matter for this example
$ rustc -O --crate-type rlib test.rs
$ objdump -CdMintel libtest.rlib 
Disassembly of section .text._ZN6myfunc20h9064a5bb1ed9f519eaaE:

0000000000000000 <myfunc::h9064a5bb1ed9f519eaa>:
   0:   67 8b 45 04             mov    eax,DWORD PTR [ebp+0x4]
   4:   c3                      ret    

And there you go. Exactly what you wanted, without a naked function.

@tari
Copy link
Contributor Author

tari commented Jul 14, 2015

It seems we have two somewhat distinct use cases for naked functions, where I only had the first in mind while writing this RFC:

  • Papering over holes in the compiler's ABI support (allowing a user to handle the ABI themselves).
  • Implementing optimized or machine-specific functions.

Neither of these works well with the current proposal, because the ABI of function calls generated by the compiler is unspecified. For a compiler-generated call to such a function, we should require that the ABI in use be well-defined (specifically it must not use the Rust ABI, and we may want to restrict it to extern "C" only). This matches the semantics of naked functions in C, where the ABI of a given function is always known from source code and platform alone, and is usually (but not always) the default calling convention.

If the calling convention of a naked function which is called from Rust code is constrained to be well-defined, the problems are purely in implementation- rustc must learn how to use non-Rust calling conventions for functions it defines (rather than ones it merely links against).


@main-- that may be technically possible, but it's too fragile for me to be comfortable encouraging such a thing. We can make the static guarantees of such a function stronger by allowing it to be declared as naked without affecting the rest of the language, so I believe we should.

@main--
Copy link

main-- commented Jul 14, 2015

I agree that especially for things like interrupt handlers, naked functions are simply a necessity. Of course one could try and hack around that by calling into an embedded inline asm block, but that's far from perfect.

I do have doubts regarding ABI emulation (providing functions in a specific ABI without compiler support): Every function needs its own trampoline, right? So one would probably write a macro to create all those trampolines which ... surprisingly isn't even that messy, but still less pretty than it could be (extern "fancyabi").

When calling a method, we need to know how. In case of a "standard" naked function, this gets us into a funny situation: Just like with any normal function, we're supposed to use the Rust ABI which is fine for us - but at the same time we're sure that they (callee) can never handle this correctly due to that ABI being undefined. I'd suggest making those functions uncallable and forcing them to be parameterless.

On the other hand, when a naked function's ABI has a well-known definition (like extern "C"), all is well; no need to impose any further restrictions.

The other difficult part are local variables. Should you write "heavy" rust code within naked functions? Probably not. But it should definitely be possible within certain limits. Why else would you use Rust if you could just write your function in plain asm (much better tooling than inline asm)? Straight up banning locals is not a great solution, but since the compiler can't really reserve stack space either (naked --> no prolog) something like MSVC's __LOCAL_SIZE is probably inevitable. The really ugly part about that particular approach is that the compiler is forced to emit even useless prologs and epilogs (when no stack spilling is required). So one can either sacrifice performance by having them or sacrifice non-optimized builds altogether since rustc (like many compilers) likes to just hold everything on the stack and then rely heavily on the optimizer to eliminate these useless stack operations, simply resulting in broken code without that.

@aidancully
Copy link

Sorry for this, but... is this a problem that needs solving? I do a lot of systems programming, and do my best to avoid inline assembly where I can. Usually, what I'll do instead (especially for something like an interrupt handler), is write an assembly file and have it call out to a C-ABI routine to get a trampoline to more maintainable code. I don't yet see how #[naked] is technically superior to just writing assembly when you want to use assembly, and then interacting through an FFI?

@eddyb
Copy link
Member

eddyb commented Jul 16, 2015

@aidancully arbitrary codegen driven by Rust code, basically.
I don't know why this is not mentioned, but being able to output naked functions from macros and even monomorphization is quite useful:

static HANDLERS: [AtomicPtr<()>; 256] = [AtomicPtr::new(); 256];
#[naked]
unsafe fn handle_interrupt<F: Fn()>(number: u8) {
    (*(HANDLERS[number as usize].load(Relaxed) as *const F))();
    asm!("iret");
}

@nikomatsakis
Copy link
Contributor

Are there links to LLVM discussion on this topic?

@tari
Copy link
Contributor Author

tari commented Jul 16, 2015

I haven't found any meaningful LLVM discussion on it. Support was added in 2009, r76198, which may be a good starting point for searching.

Also expand motivation beyond ISRs per RFC discussion.
@Tobba
Copy link

Tobba commented Jul 16, 2015

@eddyb That code is most certainly broken. If the compiler ever spills a register, you're fucked. Since it's also an interrupt handler, it should avoid clobbering registers, which is impossible to do efficiently in any way that will please LLVM.

In general the only way you're ever going to get the correct output is by having a single asm block within the function, which is just a hacky version of module-level assembly.

@main--
Copy link

main-- commented Jul 17, 2015

If the compiler ever spills a register, you're fucked.

While the RFC currently doesn't address this issue it's not a problem with naked functions in general as solving it is possible (and probably necessary; see my previous comment).

@aidancully
Copy link

@eddyb For what it's worth, I find that type of use plausible as a motivator, but I also worry that it might be very challenging to make #[naked] functions work in a generic fashion. And in that specific case, I don't think I'd write that function in a generic way: why should I need to monomorphize a function that just dereferences a function pointer and calls it? The code that will be generated during monomorphization will likely be identical, I don't want multiple copies! And, I want to make sure that the way I call the function will be valid, which can be hard to do without knowing the ABI of the called routine... But if I need to know the ABI, do I possibly require a prologue or epilogue for some types of function call? Maybe there's a solution that allows us to use the ABI as a parameter? Maybe something like this:

static HANDLERS: [AtomicPtr<()>; 256] = [AtomicPtr::new(); 256];
#[naked]
unsafe fn handle_interrupt<F: Fn()>(number: u8)
{
    // F::ABI's static functions are #[naked] #[inline].
    F::ABI::create_calling_env();
    (*(HANDLERS[number as usize].load(Relaxed) as *const F))();
    F::ABI::destroy_calling_env();
    asm!("iret");
}

But if this sort of thing is necessary to safely support generic naked functions, it'd probably need to be reflected in the RFC. (Note that I don't think the whole change would need to land at once, the initial patch sounds pretty small and may be acceptable in the short term. But the design space in which the feature would be used probably needs to be explored better... I think there's a significant risk that, even though #[naked] functions themselves are pretty simple, making them useful in a Rust context might get pretty complicated.)

@eddyb
Copy link
Member

eddyb commented Jul 18, 2015

@aidancully if you don't generate multiple monomorphizations, you need to use indirect calls, which might be a measurable cost during interrupt handling - but it is a micro-optimization and I could very well be wrong about it.

@tari
Copy link
Contributor Author

tari commented Jul 18, 2015

If I understand the current tack of the discussion now, the main concern is that the effect of writing code that is not inline assembly in a naked function is unpredictable. The correctness of the handle_interrupt example depends entirely on what codegen yields as currently proposed. Some users might be willing to accept the risk of doing so in the name of small gains, but doing so is fundamentally unsafe. This is the logic behind the current requirement that the body of a naked function contain only unsafe statements- inline assembly already requires unsafe, and the result of anything else cannot be guaranteed by the compiler.

A way to ask the compiler how much space it needs for locals would permit a solution, but there's no mechanism to enable that in LLVM. I see a way forward in having an LLVM intrinsic function that is guaranteed to lower to a constant load representing the necessary size of locals, but I think such a significant change is beyond the scope of this proposal.

With the __LOCAL_SIZE-like solution off the table, the only way to enforce (relative) safety is if only assembly is permitted in naked functions. This approach gives the compiler freedom to implement such a thing as either a module-level assembly block (with name mangling) or a true naked function, while permitting future extensions to relax the requirements (with a __LOCAL_SIZE workalike for example). On the downside, users willing to accept a certain amount of undefined behavior (trusting the compiler's behavior will be stable enough to make use of) can't choose to make that tradeoff anymore.

@tari
Copy link
Contributor Author

tari commented Jul 18, 2015

On the downside, users willing to accept a certain amount of undefined behavior (trusting the compiler's behavior will be stable enough to make use of) can't choose to make that tradeoff anymore.

Both cases could be accommodated by putting support for Rust statements in naked functions behind a feature gate; something like error: Rust statements in naked functions are extremely unsafe and may require additional guarantees in the future. This permits the statically-safe version (assuming the programmer doesn't write bugs) with inline assembly only, while allowing adventurous users to be extremely unsafe until a better solution can be chosen.

@nikomatsakis
Copy link
Contributor

In a recent lang subteam meeting, we decided to close this PR. This RFC seems to have a lot in common with #55, which was closed by @brson with the following rationale:

So far we've resisted exposing platform-specific linkage options, besides the limited ability to declare weak extern statics. If we were to pursue that I think I would want a more comprehensive proposal.

Naked functions clearly have some use but there is a workaround of just writing it in assembly.

The situation seems not to have changed very much -- basically it seems a bit premature to be introducing these sorts of constructs. The concerns about running Rust code in naked functions seem particularly salient.

(Taking off my lang subteam hat for a moment and speaking personally, I think I would prefer to address this by writing naked functions in pure assembly and linking them in with LTO -- or having a variant on inline assembly that generates an entire function.)

@rustonaut
Copy link

Given that rust is meant to be a system level programming language, I think it is needed to have this feature (or a similar on) in rust.

Through it is needed for low level tasks for many people given that this feature is not needed or even bad style in less low level code I would propose that (if available in stable) there is per default lint like #[deny(naket_fn)] or similar (or it just stays featur-gated forever, would probably be fine too?).

Also even through module level asm seems to* be able to emulate naked fn's I think naked fn's are much clearer for use cases like interrupts.

(* I might have missed something, but naked fn's can contains (some) "normal" rust code, but module level asm can't, or?)

@eddyb
Copy link
Member

eddyb commented Mar 21, 2016

@Naicode Naked functions have to be assembly-only.
However, platform-specific interrupt ABIs would be much more useful for interrupt handlers, as you could have Rust code inside them and not care about, e.g. needing iret to return.

@rustonaut
Copy link

Ok, so module level asm and naked functions most likely can emulate each other.

Hm, I't might be able to implement platform-specific interrupt handling by combining macros with naked functions, leaving naked functions still there for the other use cases.
(through not implementing them as simple rust macros might enable a higher degree of validity checking, my interrupt related knowledge is a bit limited, so I can't judge if it is possible)

@eddyb
Copy link
Member

eddyb commented Mar 21, 2016

@Naicode You would want to implement the whole Rust language in macros?

@rustonaut
Copy link

@eddyb No! that would be horribly ;=)

What I meant, is that, if the naked fn's can only contain asm, you would either write the complete ISR in assembly or delegate to a rust-fn at some point. This boiler part could be be written as a macro...

e.g. the example from the rfc:

#[naked]
#[cfg(target_arch="x86")]
unsafe fn isr_3() {
    asm!("pushad
          call increment_breakpoint_count
          popad
          iretd" :::: "volatile");
    intrinsics::unreachable();
}

could be written like:

delegate_isr_3!(increment_breakpoint_count)

Through in the end having a rust native ISR ABI might be more performant/useful

Also you could pass a codeblock to a macro, but it probably wouldn't be a good idea to use it without wrapping it in a function because it might contain a return statement (and possible other "bad" thinks), but you could also have a way to forbid return statements in code block passed to macro, well that is a different topic.

@nikomatsakis
Copy link
Contributor

Huzzah! The @rust-lang/lang team has decided to accept this RFC. As previously stated, this is being accepted on a particularly experimental basis, especially with respect to the semantics (or lack thereof) of Rust code embedded in a naked function. One plausible route for stability would be to stabilize the #[naked] attribute when the function contains only assembler, or meets other similar criteria -- but without having some experience with the attribute, we'll never know what those criteria ought to be.

@nikomatsakis
Copy link
Contributor

Tracking issue rust-lang/rust#32408.

@ticki
Copy link
Contributor

ticki commented Mar 21, 2016

Thanks, thanks, thanks, @rust-lang/lang.

@ticki
Copy link
Contributor

ticki commented Mar 21, 2016

PR submitted: rust-lang/rust#32410

bors added a commit to rust-lang/rust that referenced this pull request Mar 23, 2016
Add support for naked functions

See rust-lang/rfcs#1201 (comment)

This PR adds `#[naked]` for marking naked functions.
@tari tari mentioned this pull request May 23, 2016
@hawkw hawkw mentioned this pull request Jun 1, 2016
4 tasks
@Centril Centril added A-typesystem Type system related proposals & ideas A-machine Proposals relating to Rust's abstract machine. A-ASM Proposals related to embedding assemly into Rust. labels Nov 23, 2018
jieyouxu added a commit to jieyouxu/rust that referenced this pull request Apr 21, 2025
…ns, r=tgross35,Amanieu,traviscross

Stabilize `naked_functions`

tracking issue: rust-lang#90957
request for stabilization on tracking issue: rust-lang#90957 (comment)
reference PR: rust-lang/reference#1689

# Request for Stabilization

Two years later, we're ready to try this again. Even though this issue is already marked as having passed FCP, given the amount of time that has passed and the changes in implementation strategy, we should follow the process again.

## Summary

The `naked_functions` feature has two main parts: the `#[naked]` function attribute, and the `naked_asm!` macro.

An example of a naked function:

```rust
const THREE: usize = 3;

#[naked]
pub extern "sysv64" fn add_n(number: usize) -> usize {
    // SAFETY: the validity of the used registers
    // is guaranteed according to the "sysv64" ABI
    unsafe {
        core::arch::naked_asm!(
            "add rdi, {}",
            "mov rax, rdi",
            "ret",
            const THREE,
        )
    }
}
```

When the `#[naked]` attribute is applied to a function, the compiler won't emit a [function prologue](https://en.wikipedia.org/wiki/Function_prologue_and_epilogue) or epilogue when generating code for this function. This attribute is analogous to [`__attribute__((naked))`](https://developer.arm.com/documentation/100067/0608/Compiler-specific-Function--Variable--and-Type-Attributes/--attribute----naked---function-attribute) in C. The use of this feature allows the programmer to have precise control over the assembly that is generated for a given function.

The body of a naked function must consist of a single `naked_asm!` invocation, a heavily restricted variant of the `asm!` macro: the only legal operands are `const` and `sym`, and the only legal options are `raw` and `att_syntax`. In lieu of specifying operands, the `naked_asm!` within a naked function relies on the function's calling convention to determine the validity of registers.

## Documentation

The Rust Reference: rust-lang/reference#1689
(Previous PR: rust-lang/reference#1153)

## Tests

* [tests/run-make/naked-symbol-visiblity](https://github.com/rust-lang/rust/tree/master/tests/codegen/naked-fn) verifies that `pub`, `#[no_mangle]` and `#[linkage = "..."]` work correctly for naked functions
* [tests/codegen/naked-fn](https://github.com/rust-lang/rust/tree/master/tests/codegen/naked-fn) has tests for function alignment, use of generics, and validates the exact assembly output on linux, macos, windows and thumb
* [tests/ui/asm/naked-*](https://github.com/rust-lang/rust/tree/master/tests/ui/asm) tests for incompatible attributes, generating errors around incorrect use of `naked_asm!`, etc

## Interaction with other (unstable) features

### [fn_align](rust-lang#82232)

Combining `#[naked]` with `#[repr(align(N))]` works well, and is tested e.g. here

- https://github.com/rust-lang/rust/blob/master/tests/codegen/naked-fn/aligned.rs
- https://github.com/rust-lang/rust/blob/master/tests/codegen/naked-fn/min-function-alignment.rs

It's tested extensively because we do need to explicitly support the `repr(align)` attribute (and make sure we e.g. don't mistake powers of two for number of bytes).

## History

This feature was originally proposed in [RFC 1201](rust-lang/rfcs#1201), filed on 2015-07-10 and accepted on 2016-03-21. Support for this feature was added in [rust-lang#32410](rust-lang#32410), landing on 2016-03-23. Development languished for several years as it was realized that the semantics given in RFC 1201 were insufficiently specific. To address this, a minimal subset of naked functions was specified by [RFC 2972](rust-lang/rfcs#2972), filed on 2020-08-07 and accepted on 2021-11-16. Prior to the acceptance of RFC 2972, all of the stricter behavior specified by RFC 2972 was implemented as a series of warn-by-default lints that would trigger on existing uses of the `naked` attribute; these lints became hard errors in [rust-lang#93153](rust-lang#93153) on 2022-01-22. As a result, today RFC 2972 has completely superseded RFC 1201 in describing the semantics of the `naked` attribute.

More recently, the `naked_asm!` macro was added to replace the earlier use of a heavily restricted `asm!` invocation. The `naked_asm!` name is clearer in error messages, and provides a place for documenting the specific requirements of inline assembly in naked functions.

The implementation strategy was changed to emitting a global assembly block. In effect, an extern function

```rust
extern "C" fn foo() {
    core::arch::naked_asm!("ret")
}
```

is emitted as something similar to

```rust
core::arch::global_asm!(
    "foo:",
    "ret"
);

extern "C" {
    fn foo();
}
```

The codegen approach was chosen over the llvm naked function attribute because:

- the rust compiler can guarantee the behavior (no sneaky additional instructions, no inlining, etc.)
- behavior is the same on all backends (llvm, cranelift, gcc, etc)

Finally, there is now an allow list of compatible attributes on naked functions, so that e.g. `#[inline]` is rejected with an error. The `#[target_feature]` attribute on naked functions was later made separately unstable, because implementing it is complex and we did not want to block naked functions themselves on how target features work on them. See also rust-lang#138568.

relevant PRs for these recent changes

- rust-lang#127853
- rust-lang#128651
- rust-lang#128004
- rust-lang#138570
-
### Various historical notes

#### `noreturn`
[RFC 2972](https://github.com/rust-lang/rfcs/blob/master/text/2972-constrained-naked.md) mentions that naked functions

> must have a body which contains only a single asm!() statement which:
> iii. must contain the noreturn option.

Instead of `asm!`, the current implementation mandates that the body contain a single `naked_asm!` statement. The `naked_asm!` macro is a heavily restricted version of the `asm!` macro, making it easier to talk about and document the rules of assembly in naked functions and give dedicated error messages.

For `naked_asm!`, the behavior of the `asm!`'s `noreturn` option is implicit. The `noreturn` option means that it is UB for control flow to fall through the end of the assembly block. With `asm!`, this option is usually used for blocks that diverge (and thus have no return and can be typed as `!`). With `naked_asm!`, the intent is different: usually naked funtions do return, but they must do so from within the assembly block. The `noreturn` option was used so that the compiler would not itself also insert a `ret` instruction at the very end.

#### padding / `ud2`

A `naked_asm!` block that violates the safety assumption that control flow must not fall through the end of the assembly block is UB. Because no return instruction is emitted, whatever bytes follow the naked function will be executed, resulting in truly undefined behavior. There has been discussion whether rustc should emit an invalid instruction (e.g. `ud2`  on x86) after the `naked_asm!` block to at least fail early in the case of an invalid `naked_asm!`. It was however decided that it is more useful to guarantee that `#[naked]` functions NEVER contain any instructions besides those in the `naked_asm!` block.

# unresolved questions

None

r? `@Amanieu`

I've validated the tests on x86_64 and aarch64
ChrisDenton added a commit to ChrisDenton/rust that referenced this pull request Apr 21, 2025
…ns, r=tgross35,Amanieu,traviscross

Stabilize `naked_functions`

tracking issue: rust-lang#90957
request for stabilization on tracking issue: rust-lang#90957 (comment)
reference PR: rust-lang/reference#1689

# Request for Stabilization

Two years later, we're ready to try this again. Even though this issue is already marked as having passed FCP, given the amount of time that has passed and the changes in implementation strategy, we should follow the process again.

## Summary

The `naked_functions` feature has two main parts: the `#[naked]` function attribute, and the `naked_asm!` macro.

An example of a naked function:

```rust
const THREE: usize = 3;

#[naked]
pub extern "sysv64" fn add_n(number: usize) -> usize {
    // SAFETY: the validity of the used registers
    // is guaranteed according to the "sysv64" ABI
    unsafe {
        core::arch::naked_asm!(
            "add rdi, {}",
            "mov rax, rdi",
            "ret",
            const THREE,
        )
    }
}
```

When the `#[naked]` attribute is applied to a function, the compiler won't emit a [function prologue](https://en.wikipedia.org/wiki/Function_prologue_and_epilogue) or epilogue when generating code for this function. This attribute is analogous to [`__attribute__((naked))`](https://developer.arm.com/documentation/100067/0608/Compiler-specific-Function--Variable--and-Type-Attributes/--attribute----naked---function-attribute) in C. The use of this feature allows the programmer to have precise control over the assembly that is generated for a given function.

The body of a naked function must consist of a single `naked_asm!` invocation, a heavily restricted variant of the `asm!` macro: the only legal operands are `const` and `sym`, and the only legal options are `raw` and `att_syntax`. In lieu of specifying operands, the `naked_asm!` within a naked function relies on the function's calling convention to determine the validity of registers.

## Documentation

The Rust Reference: rust-lang/reference#1689
(Previous PR: rust-lang/reference#1153)

## Tests

* [tests/run-make/naked-symbol-visiblity](https://github.com/rust-lang/rust/tree/master/tests/codegen/naked-fn) verifies that `pub`, `#[no_mangle]` and `#[linkage = "..."]` work correctly for naked functions
* [tests/codegen/naked-fn](https://github.com/rust-lang/rust/tree/master/tests/codegen/naked-fn) has tests for function alignment, use of generics, and validates the exact assembly output on linux, macos, windows and thumb
* [tests/ui/asm/naked-*](https://github.com/rust-lang/rust/tree/master/tests/ui/asm) tests for incompatible attributes, generating errors around incorrect use of `naked_asm!`, etc

## Interaction with other (unstable) features

### [fn_align](rust-lang#82232)

Combining `#[naked]` with `#[repr(align(N))]` works well, and is tested e.g. here

- https://github.com/rust-lang/rust/blob/master/tests/codegen/naked-fn/aligned.rs
- https://github.com/rust-lang/rust/blob/master/tests/codegen/naked-fn/min-function-alignment.rs

It's tested extensively because we do need to explicitly support the `repr(align)` attribute (and make sure we e.g. don't mistake powers of two for number of bytes).

## History

This feature was originally proposed in [RFC 1201](rust-lang/rfcs#1201), filed on 2015-07-10 and accepted on 2016-03-21. Support for this feature was added in [rust-lang#32410](rust-lang#32410), landing on 2016-03-23. Development languished for several years as it was realized that the semantics given in RFC 1201 were insufficiently specific. To address this, a minimal subset of naked functions was specified by [RFC 2972](rust-lang/rfcs#2972), filed on 2020-08-07 and accepted on 2021-11-16. Prior to the acceptance of RFC 2972, all of the stricter behavior specified by RFC 2972 was implemented as a series of warn-by-default lints that would trigger on existing uses of the `naked` attribute; these lints became hard errors in [rust-lang#93153](rust-lang#93153) on 2022-01-22. As a result, today RFC 2972 has completely superseded RFC 1201 in describing the semantics of the `naked` attribute.

More recently, the `naked_asm!` macro was added to replace the earlier use of a heavily restricted `asm!` invocation. The `naked_asm!` name is clearer in error messages, and provides a place for documenting the specific requirements of inline assembly in naked functions.

The implementation strategy was changed to emitting a global assembly block. In effect, an extern function

```rust
extern "C" fn foo() {
    core::arch::naked_asm!("ret")
}
```

is emitted as something similar to

```rust
core::arch::global_asm!(
    "foo:",
    "ret"
);

extern "C" {
    fn foo();
}
```

The codegen approach was chosen over the llvm naked function attribute because:

- the rust compiler can guarantee the behavior (no sneaky additional instructions, no inlining, etc.)
- behavior is the same on all backends (llvm, cranelift, gcc, etc)

Finally, there is now an allow list of compatible attributes on naked functions, so that e.g. `#[inline]` is rejected with an error. The `#[target_feature]` attribute on naked functions was later made separately unstable, because implementing it is complex and we did not want to block naked functions themselves on how target features work on them. See also rust-lang#138568.

relevant PRs for these recent changes

- rust-lang#127853
- rust-lang#128651
- rust-lang#128004
- rust-lang#138570
-
### Various historical notes

#### `noreturn`
[RFC 2972](https://github.com/rust-lang/rfcs/blob/master/text/2972-constrained-naked.md) mentions that naked functions

> must have a body which contains only a single asm!() statement which:
> iii. must contain the noreturn option.

Instead of `asm!`, the current implementation mandates that the body contain a single `naked_asm!` statement. The `naked_asm!` macro is a heavily restricted version of the `asm!` macro, making it easier to talk about and document the rules of assembly in naked functions and give dedicated error messages.

For `naked_asm!`, the behavior of the `asm!`'s `noreturn` option is implicit. The `noreturn` option means that it is UB for control flow to fall through the end of the assembly block. With `asm!`, this option is usually used for blocks that diverge (and thus have no return and can be typed as `!`). With `naked_asm!`, the intent is different: usually naked funtions do return, but they must do so from within the assembly block. The `noreturn` option was used so that the compiler would not itself also insert a `ret` instruction at the very end.

#### padding / `ud2`

A `naked_asm!` block that violates the safety assumption that control flow must not fall through the end of the assembly block is UB. Because no return instruction is emitted, whatever bytes follow the naked function will be executed, resulting in truly undefined behavior. There has been discussion whether rustc should emit an invalid instruction (e.g. `ud2`  on x86) after the `naked_asm!` block to at least fail early in the case of an invalid `naked_asm!`. It was however decided that it is more useful to guarantee that `#[naked]` functions NEVER contain any instructions besides those in the `naked_asm!` block.

# unresolved questions

None

r? ``@Amanieu``

I've validated the tests on x86_64 and aarch64
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Apr 21, 2025
Rollup merge of rust-lang#134213 - folkertdev:stabilize-naked-functions, r=tgross35,Amanieu,traviscross

Stabilize `naked_functions`

tracking issue: rust-lang#90957
request for stabilization on tracking issue: rust-lang#90957 (comment)
reference PR: rust-lang/reference#1689

# Request for Stabilization

Two years later, we're ready to try this again. Even though this issue is already marked as having passed FCP, given the amount of time that has passed and the changes in implementation strategy, we should follow the process again.

## Summary

The `naked_functions` feature has two main parts: the `#[naked]` function attribute, and the `naked_asm!` macro.

An example of a naked function:

```rust
const THREE: usize = 3;

#[naked]
pub extern "sysv64" fn add_n(number: usize) -> usize {
    // SAFETY: the validity of the used registers
    // is guaranteed according to the "sysv64" ABI
    unsafe {
        core::arch::naked_asm!(
            "add rdi, {}",
            "mov rax, rdi",
            "ret",
            const THREE,
        )
    }
}
```

When the `#[naked]` attribute is applied to a function, the compiler won't emit a [function prologue](https://en.wikipedia.org/wiki/Function_prologue_and_epilogue) or epilogue when generating code for this function. This attribute is analogous to [`__attribute__((naked))`](https://developer.arm.com/documentation/100067/0608/Compiler-specific-Function--Variable--and-Type-Attributes/--attribute----naked---function-attribute) in C. The use of this feature allows the programmer to have precise control over the assembly that is generated for a given function.

The body of a naked function must consist of a single `naked_asm!` invocation, a heavily restricted variant of the `asm!` macro: the only legal operands are `const` and `sym`, and the only legal options are `raw` and `att_syntax`. In lieu of specifying operands, the `naked_asm!` within a naked function relies on the function's calling convention to determine the validity of registers.

## Documentation

The Rust Reference: rust-lang/reference#1689
(Previous PR: rust-lang/reference#1153)

## Tests

* [tests/run-make/naked-symbol-visiblity](https://github.com/rust-lang/rust/tree/master/tests/codegen/naked-fn) verifies that `pub`, `#[no_mangle]` and `#[linkage = "..."]` work correctly for naked functions
* [tests/codegen/naked-fn](https://github.com/rust-lang/rust/tree/master/tests/codegen/naked-fn) has tests for function alignment, use of generics, and validates the exact assembly output on linux, macos, windows and thumb
* [tests/ui/asm/naked-*](https://github.com/rust-lang/rust/tree/master/tests/ui/asm) tests for incompatible attributes, generating errors around incorrect use of `naked_asm!`, etc

## Interaction with other (unstable) features

### [fn_align](rust-lang#82232)

Combining `#[naked]` with `#[repr(align(N))]` works well, and is tested e.g. here

- https://github.com/rust-lang/rust/blob/master/tests/codegen/naked-fn/aligned.rs
- https://github.com/rust-lang/rust/blob/master/tests/codegen/naked-fn/min-function-alignment.rs

It's tested extensively because we do need to explicitly support the `repr(align)` attribute (and make sure we e.g. don't mistake powers of two for number of bytes).

## History

This feature was originally proposed in [RFC 1201](rust-lang/rfcs#1201), filed on 2015-07-10 and accepted on 2016-03-21. Support for this feature was added in [rust-lang#32410](rust-lang#32410), landing on 2016-03-23. Development languished for several years as it was realized that the semantics given in RFC 1201 were insufficiently specific. To address this, a minimal subset of naked functions was specified by [RFC 2972](rust-lang/rfcs#2972), filed on 2020-08-07 and accepted on 2021-11-16. Prior to the acceptance of RFC 2972, all of the stricter behavior specified by RFC 2972 was implemented as a series of warn-by-default lints that would trigger on existing uses of the `naked` attribute; these lints became hard errors in [rust-lang#93153](rust-lang#93153) on 2022-01-22. As a result, today RFC 2972 has completely superseded RFC 1201 in describing the semantics of the `naked` attribute.

More recently, the `naked_asm!` macro was added to replace the earlier use of a heavily restricted `asm!` invocation. The `naked_asm!` name is clearer in error messages, and provides a place for documenting the specific requirements of inline assembly in naked functions.

The implementation strategy was changed to emitting a global assembly block. In effect, an extern function

```rust
extern "C" fn foo() {
    core::arch::naked_asm!("ret")
}
```

is emitted as something similar to

```rust
core::arch::global_asm!(
    "foo:",
    "ret"
);

extern "C" {
    fn foo();
}
```

The codegen approach was chosen over the llvm naked function attribute because:

- the rust compiler can guarantee the behavior (no sneaky additional instructions, no inlining, etc.)
- behavior is the same on all backends (llvm, cranelift, gcc, etc)

Finally, there is now an allow list of compatible attributes on naked functions, so that e.g. `#[inline]` is rejected with an error. The `#[target_feature]` attribute on naked functions was later made separately unstable, because implementing it is complex and we did not want to block naked functions themselves on how target features work on them. See also rust-lang#138568.

relevant PRs for these recent changes

- rust-lang#127853
- rust-lang#128651
- rust-lang#128004
- rust-lang#138570
-
### Various historical notes

#### `noreturn`
[RFC 2972](https://github.com/rust-lang/rfcs/blob/master/text/2972-constrained-naked.md) mentions that naked functions

> must have a body which contains only a single asm!() statement which:
> iii. must contain the noreturn option.

Instead of `asm!`, the current implementation mandates that the body contain a single `naked_asm!` statement. The `naked_asm!` macro is a heavily restricted version of the `asm!` macro, making it easier to talk about and document the rules of assembly in naked functions and give dedicated error messages.

For `naked_asm!`, the behavior of the `asm!`'s `noreturn` option is implicit. The `noreturn` option means that it is UB for control flow to fall through the end of the assembly block. With `asm!`, this option is usually used for blocks that diverge (and thus have no return and can be typed as `!`). With `naked_asm!`, the intent is different: usually naked funtions do return, but they must do so from within the assembly block. The `noreturn` option was used so that the compiler would not itself also insert a `ret` instruction at the very end.

#### padding / `ud2`

A `naked_asm!` block that violates the safety assumption that control flow must not fall through the end of the assembly block is UB. Because no return instruction is emitted, whatever bytes follow the naked function will be executed, resulting in truly undefined behavior. There has been discussion whether rustc should emit an invalid instruction (e.g. `ud2`  on x86) after the `naked_asm!` block to at least fail early in the case of an invalid `naked_asm!`. It was however decided that it is more useful to guarantee that `#[naked]` functions NEVER contain any instructions besides those in the `naked_asm!` block.

# unresolved questions

None

r? ``@Amanieu``

I've validated the tests on x86_64 and aarch64
github-actions bot pushed a commit to rust-lang/miri that referenced this pull request Apr 22, 2025
…oss35,Amanieu,traviscross

Stabilize `naked_functions`

tracking issue: rust-lang/rust#90957
request for stabilization on tracking issue: rust-lang/rust#90957 (comment)
reference PR: rust-lang/reference#1689

# Request for Stabilization

Two years later, we're ready to try this again. Even though this issue is already marked as having passed FCP, given the amount of time that has passed and the changes in implementation strategy, we should follow the process again.

## Summary

The `naked_functions` feature has two main parts: the `#[naked]` function attribute, and the `naked_asm!` macro.

An example of a naked function:

```rust
const THREE: usize = 3;

#[naked]
pub extern "sysv64" fn add_n(number: usize) -> usize {
    // SAFETY: the validity of the used registers
    // is guaranteed according to the "sysv64" ABI
    unsafe {
        core::arch::naked_asm!(
            "add rdi, {}",
            "mov rax, rdi",
            "ret",
            const THREE,
        )
    }
}
```

When the `#[naked]` attribute is applied to a function, the compiler won't emit a [function prologue](https://en.wikipedia.org/wiki/Function_prologue_and_epilogue) or epilogue when generating code for this function. This attribute is analogous to [`__attribute__((naked))`](https://developer.arm.com/documentation/100067/0608/Compiler-specific-Function--Variable--and-Type-Attributes/--attribute----naked---function-attribute) in C. The use of this feature allows the programmer to have precise control over the assembly that is generated for a given function.

The body of a naked function must consist of a single `naked_asm!` invocation, a heavily restricted variant of the `asm!` macro: the only legal operands are `const` and `sym`, and the only legal options are `raw` and `att_syntax`. In lieu of specifying operands, the `naked_asm!` within a naked function relies on the function's calling convention to determine the validity of registers.

## Documentation

The Rust Reference: rust-lang/reference#1689
(Previous PR: rust-lang/reference#1153)

## Tests

* [tests/run-make/naked-symbol-visiblity](https://github.com/rust-lang/rust/tree/master/tests/codegen/naked-fn) verifies that `pub`, `#[no_mangle]` and `#[linkage = "..."]` work correctly for naked functions
* [tests/codegen/naked-fn](https://github.com/rust-lang/rust/tree/master/tests/codegen/naked-fn) has tests for function alignment, use of generics, and validates the exact assembly output on linux, macos, windows and thumb
* [tests/ui/asm/naked-*](https://github.com/rust-lang/rust/tree/master/tests/ui/asm) tests for incompatible attributes, generating errors around incorrect use of `naked_asm!`, etc

## Interaction with other (unstable) features

### [fn_align](rust-lang/rust#82232)

Combining `#[naked]` with `#[repr(align(N))]` works well, and is tested e.g. here

- https://github.com/rust-lang/rust/blob/master/tests/codegen/naked-fn/aligned.rs
- https://github.com/rust-lang/rust/blob/master/tests/codegen/naked-fn/min-function-alignment.rs

It's tested extensively because we do need to explicitly support the `repr(align)` attribute (and make sure we e.g. don't mistake powers of two for number of bytes).

## History

This feature was originally proposed in [RFC 1201](rust-lang/rfcs#1201), filed on 2015-07-10 and accepted on 2016-03-21. Support for this feature was added in [#32410](rust-lang/rust#32410), landing on 2016-03-23. Development languished for several years as it was realized that the semantics given in RFC 1201 were insufficiently specific. To address this, a minimal subset of naked functions was specified by [RFC 2972](rust-lang/rfcs#2972), filed on 2020-08-07 and accepted on 2021-11-16. Prior to the acceptance of RFC 2972, all of the stricter behavior specified by RFC 2972 was implemented as a series of warn-by-default lints that would trigger on existing uses of the `naked` attribute; these lints became hard errors in [#93153](rust-lang/rust#93153) on 2022-01-22. As a result, today RFC 2972 has completely superseded RFC 1201 in describing the semantics of the `naked` attribute.

More recently, the `naked_asm!` macro was added to replace the earlier use of a heavily restricted `asm!` invocation. The `naked_asm!` name is clearer in error messages, and provides a place for documenting the specific requirements of inline assembly in naked functions.

The implementation strategy was changed to emitting a global assembly block. In effect, an extern function

```rust
extern "C" fn foo() {
    core::arch::naked_asm!("ret")
}
```

is emitted as something similar to

```rust
core::arch::global_asm!(
    "foo:",
    "ret"
);

extern "C" {
    fn foo();
}
```

The codegen approach was chosen over the llvm naked function attribute because:

- the rust compiler can guarantee the behavior (no sneaky additional instructions, no inlining, etc.)
- behavior is the same on all backends (llvm, cranelift, gcc, etc)

Finally, there is now an allow list of compatible attributes on naked functions, so that e.g. `#[inline]` is rejected with an error. The `#[target_feature]` attribute on naked functions was later made separately unstable, because implementing it is complex and we did not want to block naked functions themselves on how target features work on them. See also rust-lang/rust#138568.

relevant PRs for these recent changes

- rust-lang/rust#127853
- rust-lang/rust#128651
- rust-lang/rust#128004
- rust-lang/rust#138570
-
### Various historical notes

#### `noreturn`
[RFC 2972](https://github.com/rust-lang/rfcs/blob/master/text/2972-constrained-naked.md) mentions that naked functions

> must have a body which contains only a single asm!() statement which:
> iii. must contain the noreturn option.

Instead of `asm!`, the current implementation mandates that the body contain a single `naked_asm!` statement. The `naked_asm!` macro is a heavily restricted version of the `asm!` macro, making it easier to talk about and document the rules of assembly in naked functions and give dedicated error messages.

For `naked_asm!`, the behavior of the `asm!`'s `noreturn` option is implicit. The `noreturn` option means that it is UB for control flow to fall through the end of the assembly block. With `asm!`, this option is usually used for blocks that diverge (and thus have no return and can be typed as `!`). With `naked_asm!`, the intent is different: usually naked funtions do return, but they must do so from within the assembly block. The `noreturn` option was used so that the compiler would not itself also insert a `ret` instruction at the very end.

#### padding / `ud2`

A `naked_asm!` block that violates the safety assumption that control flow must not fall through the end of the assembly block is UB. Because no return instruction is emitted, whatever bytes follow the naked function will be executed, resulting in truly undefined behavior. There has been discussion whether rustc should emit an invalid instruction (e.g. `ud2`  on x86) after the `naked_asm!` block to at least fail early in the case of an invalid `naked_asm!`. It was however decided that it is more useful to guarantee that `#[naked]` functions NEVER contain any instructions besides those in the `naked_asm!` block.

# unresolved questions

None

r? ``@Amanieu``

I've validated the tests on x86_64 and aarch64
github-actions bot pushed a commit to model-checking/verify-rust-std that referenced this pull request Apr 23, 2025
…ns, r=tgross35,Amanieu,traviscross

Stabilize `naked_functions`

tracking issue: rust-lang#90957
request for stabilization on tracking issue: rust-lang#90957 (comment)
reference PR: rust-lang/reference#1689

# Request for Stabilization

Two years later, we're ready to try this again. Even though this issue is already marked as having passed FCP, given the amount of time that has passed and the changes in implementation strategy, we should follow the process again.

## Summary

The `naked_functions` feature has two main parts: the `#[naked]` function attribute, and the `naked_asm!` macro.

An example of a naked function:

```rust
const THREE: usize = 3;

#[naked]
pub extern "sysv64" fn add_n(number: usize) -> usize {
    // SAFETY: the validity of the used registers
    // is guaranteed according to the "sysv64" ABI
    unsafe {
        core::arch::naked_asm!(
            "add rdi, {}",
            "mov rax, rdi",
            "ret",
            const THREE,
        )
    }
}
```

When the `#[naked]` attribute is applied to a function, the compiler won't emit a [function prologue](https://en.wikipedia.org/wiki/Function_prologue_and_epilogue) or epilogue when generating code for this function. This attribute is analogous to [`__attribute__((naked))`](https://developer.arm.com/documentation/100067/0608/Compiler-specific-Function--Variable--and-Type-Attributes/--attribute----naked---function-attribute) in C. The use of this feature allows the programmer to have precise control over the assembly that is generated for a given function.

The body of a naked function must consist of a single `naked_asm!` invocation, a heavily restricted variant of the `asm!` macro: the only legal operands are `const` and `sym`, and the only legal options are `raw` and `att_syntax`. In lieu of specifying operands, the `naked_asm!` within a naked function relies on the function's calling convention to determine the validity of registers.

## Documentation

The Rust Reference: rust-lang/reference#1689
(Previous PR: rust-lang/reference#1153)

## Tests

* [tests/run-make/naked-symbol-visiblity](https://github.com/rust-lang/rust/tree/master/tests/codegen/naked-fn) verifies that `pub`, `#[no_mangle]` and `#[linkage = "..."]` work correctly for naked functions
* [tests/codegen/naked-fn](https://github.com/rust-lang/rust/tree/master/tests/codegen/naked-fn) has tests for function alignment, use of generics, and validates the exact assembly output on linux, macos, windows and thumb
* [tests/ui/asm/naked-*](https://github.com/rust-lang/rust/tree/master/tests/ui/asm) tests for incompatible attributes, generating errors around incorrect use of `naked_asm!`, etc

## Interaction with other (unstable) features

### [fn_align](rust-lang#82232)

Combining `#[naked]` with `#[repr(align(N))]` works well, and is tested e.g. here

- https://github.com/rust-lang/rust/blob/master/tests/codegen/naked-fn/aligned.rs
- https://github.com/rust-lang/rust/blob/master/tests/codegen/naked-fn/min-function-alignment.rs

It's tested extensively because we do need to explicitly support the `repr(align)` attribute (and make sure we e.g. don't mistake powers of two for number of bytes).

## History

This feature was originally proposed in [RFC 1201](rust-lang/rfcs#1201), filed on 2015-07-10 and accepted on 2016-03-21. Support for this feature was added in [rust-lang#32410](rust-lang#32410), landing on 2016-03-23. Development languished for several years as it was realized that the semantics given in RFC 1201 were insufficiently specific. To address this, a minimal subset of naked functions was specified by [RFC 2972](rust-lang/rfcs#2972), filed on 2020-08-07 and accepted on 2021-11-16. Prior to the acceptance of RFC 2972, all of the stricter behavior specified by RFC 2972 was implemented as a series of warn-by-default lints that would trigger on existing uses of the `naked` attribute; these lints became hard errors in [rust-lang#93153](rust-lang#93153) on 2022-01-22. As a result, today RFC 2972 has completely superseded RFC 1201 in describing the semantics of the `naked` attribute.

More recently, the `naked_asm!` macro was added to replace the earlier use of a heavily restricted `asm!` invocation. The `naked_asm!` name is clearer in error messages, and provides a place for documenting the specific requirements of inline assembly in naked functions.

The implementation strategy was changed to emitting a global assembly block. In effect, an extern function

```rust
extern "C" fn foo() {
    core::arch::naked_asm!("ret")
}
```

is emitted as something similar to

```rust
core::arch::global_asm!(
    "foo:",
    "ret"
);

extern "C" {
    fn foo();
}
```

The codegen approach was chosen over the llvm naked function attribute because:

- the rust compiler can guarantee the behavior (no sneaky additional instructions, no inlining, etc.)
- behavior is the same on all backends (llvm, cranelift, gcc, etc)

Finally, there is now an allow list of compatible attributes on naked functions, so that e.g. `#[inline]` is rejected with an error. The `#[target_feature]` attribute on naked functions was later made separately unstable, because implementing it is complex and we did not want to block naked functions themselves on how target features work on them. See also rust-lang#138568.

relevant PRs for these recent changes

- rust-lang#127853
- rust-lang#128651
- rust-lang#128004
- rust-lang#138570
-
### Various historical notes

#### `noreturn`
[RFC 2972](https://github.com/rust-lang/rfcs/blob/master/text/2972-constrained-naked.md) mentions that naked functions

> must have a body which contains only a single asm!() statement which:
> iii. must contain the noreturn option.

Instead of `asm!`, the current implementation mandates that the body contain a single `naked_asm!` statement. The `naked_asm!` macro is a heavily restricted version of the `asm!` macro, making it easier to talk about and document the rules of assembly in naked functions and give dedicated error messages.

For `naked_asm!`, the behavior of the `asm!`'s `noreturn` option is implicit. The `noreturn` option means that it is UB for control flow to fall through the end of the assembly block. With `asm!`, this option is usually used for blocks that diverge (and thus have no return and can be typed as `!`). With `naked_asm!`, the intent is different: usually naked funtions do return, but they must do so from within the assembly block. The `noreturn` option was used so that the compiler would not itself also insert a `ret` instruction at the very end.

#### padding / `ud2`

A `naked_asm!` block that violates the safety assumption that control flow must not fall through the end of the assembly block is UB. Because no return instruction is emitted, whatever bytes follow the naked function will be executed, resulting in truly undefined behavior. There has been discussion whether rustc should emit an invalid instruction (e.g. `ud2`  on x86) after the `naked_asm!` block to at least fail early in the case of an invalid `naked_asm!`. It was however decided that it is more useful to guarantee that `#[naked]` functions NEVER contain any instructions besides those in the `naked_asm!` block.

# unresolved questions

None

r? ``@Amanieu``

I've validated the tests on x86_64 and aarch64
github-actions bot pushed a commit to rust-lang/rustc-dev-guide that referenced this pull request Apr 24, 2025
…oss35,Amanieu,traviscross

Stabilize `naked_functions`

tracking issue: rust-lang/rust#90957
request for stabilization on tracking issue: rust-lang/rust#90957 (comment)
reference PR: rust-lang/reference#1689

# Request for Stabilization

Two years later, we're ready to try this again. Even though this issue is already marked as having passed FCP, given the amount of time that has passed and the changes in implementation strategy, we should follow the process again.

## Summary

The `naked_functions` feature has two main parts: the `#[naked]` function attribute, and the `naked_asm!` macro.

An example of a naked function:

```rust
const THREE: usize = 3;

#[naked]
pub extern "sysv64" fn add_n(number: usize) -> usize {
    // SAFETY: the validity of the used registers
    // is guaranteed according to the "sysv64" ABI
    unsafe {
        core::arch::naked_asm!(
            "add rdi, {}",
            "mov rax, rdi",
            "ret",
            const THREE,
        )
    }
}
```

When the `#[naked]` attribute is applied to a function, the compiler won't emit a [function prologue](https://en.wikipedia.org/wiki/Function_prologue_and_epilogue) or epilogue when generating code for this function. This attribute is analogous to [`__attribute__((naked))`](https://developer.arm.com/documentation/100067/0608/Compiler-specific-Function--Variable--and-Type-Attributes/--attribute----naked---function-attribute) in C. The use of this feature allows the programmer to have precise control over the assembly that is generated for a given function.

The body of a naked function must consist of a single `naked_asm!` invocation, a heavily restricted variant of the `asm!` macro: the only legal operands are `const` and `sym`, and the only legal options are `raw` and `att_syntax`. In lieu of specifying operands, the `naked_asm!` within a naked function relies on the function's calling convention to determine the validity of registers.

## Documentation

The Rust Reference: rust-lang/reference#1689
(Previous PR: rust-lang/reference#1153)

## Tests

* [tests/run-make/naked-symbol-visiblity](https://github.com/rust-lang/rust/tree/master/tests/codegen/naked-fn) verifies that `pub`, `#[no_mangle]` and `#[linkage = "..."]` work correctly for naked functions
* [tests/codegen/naked-fn](https://github.com/rust-lang/rust/tree/master/tests/codegen/naked-fn) has tests for function alignment, use of generics, and validates the exact assembly output on linux, macos, windows and thumb
* [tests/ui/asm/naked-*](https://github.com/rust-lang/rust/tree/master/tests/ui/asm) tests for incompatible attributes, generating errors around incorrect use of `naked_asm!`, etc

## Interaction with other (unstable) features

### [fn_align](rust-lang/rust#82232)

Combining `#[naked]` with `#[repr(align(N))]` works well, and is tested e.g. here

- https://github.com/rust-lang/rust/blob/master/tests/codegen/naked-fn/aligned.rs
- https://github.com/rust-lang/rust/blob/master/tests/codegen/naked-fn/min-function-alignment.rs

It's tested extensively because we do need to explicitly support the `repr(align)` attribute (and make sure we e.g. don't mistake powers of two for number of bytes).

## History

This feature was originally proposed in [RFC 1201](rust-lang/rfcs#1201), filed on 2015-07-10 and accepted on 2016-03-21. Support for this feature was added in [#32410](rust-lang/rust#32410), landing on 2016-03-23. Development languished for several years as it was realized that the semantics given in RFC 1201 were insufficiently specific. To address this, a minimal subset of naked functions was specified by [RFC 2972](rust-lang/rfcs#2972), filed on 2020-08-07 and accepted on 2021-11-16. Prior to the acceptance of RFC 2972, all of the stricter behavior specified by RFC 2972 was implemented as a series of warn-by-default lints that would trigger on existing uses of the `naked` attribute; these lints became hard errors in [#93153](rust-lang/rust#93153) on 2022-01-22. As a result, today RFC 2972 has completely superseded RFC 1201 in describing the semantics of the `naked` attribute.

More recently, the `naked_asm!` macro was added to replace the earlier use of a heavily restricted `asm!` invocation. The `naked_asm!` name is clearer in error messages, and provides a place for documenting the specific requirements of inline assembly in naked functions.

The implementation strategy was changed to emitting a global assembly block. In effect, an extern function

```rust
extern "C" fn foo() {
    core::arch::naked_asm!("ret")
}
```

is emitted as something similar to

```rust
core::arch::global_asm!(
    "foo:",
    "ret"
);

extern "C" {
    fn foo();
}
```

The codegen approach was chosen over the llvm naked function attribute because:

- the rust compiler can guarantee the behavior (no sneaky additional instructions, no inlining, etc.)
- behavior is the same on all backends (llvm, cranelift, gcc, etc)

Finally, there is now an allow list of compatible attributes on naked functions, so that e.g. `#[inline]` is rejected with an error. The `#[target_feature]` attribute on naked functions was later made separately unstable, because implementing it is complex and we did not want to block naked functions themselves on how target features work on them. See also rust-lang/rust#138568.

relevant PRs for these recent changes

- rust-lang/rust#127853
- rust-lang/rust#128651
- rust-lang/rust#128004
- rust-lang/rust#138570
-
### Various historical notes

#### `noreturn`
[RFC 2972](https://github.com/rust-lang/rfcs/blob/master/text/2972-constrained-naked.md) mentions that naked functions

> must have a body which contains only a single asm!() statement which:
> iii. must contain the noreturn option.

Instead of `asm!`, the current implementation mandates that the body contain a single `naked_asm!` statement. The `naked_asm!` macro is a heavily restricted version of the `asm!` macro, making it easier to talk about and document the rules of assembly in naked functions and give dedicated error messages.

For `naked_asm!`, the behavior of the `asm!`'s `noreturn` option is implicit. The `noreturn` option means that it is UB for control flow to fall through the end of the assembly block. With `asm!`, this option is usually used for blocks that diverge (and thus have no return and can be typed as `!`). With `naked_asm!`, the intent is different: usually naked funtions do return, but they must do so from within the assembly block. The `noreturn` option was used so that the compiler would not itself also insert a `ret` instruction at the very end.

#### padding / `ud2`

A `naked_asm!` block that violates the safety assumption that control flow must not fall through the end of the assembly block is UB. Because no return instruction is emitted, whatever bytes follow the naked function will be executed, resulting in truly undefined behavior. There has been discussion whether rustc should emit an invalid instruction (e.g. `ud2`  on x86) after the `naked_asm!` block to at least fail early in the case of an invalid `naked_asm!`. It was however decided that it is more useful to guarantee that `#[naked]` functions NEVER contain any instructions besides those in the `naked_asm!` block.

# unresolved questions

None

r? ``@Amanieu``

I've validated the tests on x86_64 and aarch64
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ASM Proposals related to embedding assemly into Rust. A-machine Proposals relating to Rust's abstract machine. A-typesystem Type system related proposals & ideas final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.