-
Notifications
You must be signed in to change notification settings - Fork 13.3k
#[naked]
: report incompatible attributes
#127853
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
#[naked]
: report incompatible attributes
#127853
Conversation
rustbot has assigned @compiler-errors. Use |
// | ||
// * `#[inline]` | ||
// * `#[track_caller]` | ||
// * `#[target_feature]` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#[target_feature]
affects the set of instructions allowed inside inline assembly as well as the exact encoding of certain instructions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I'm going off of #90957 (comment) where @joshtriplett suggests to exclude #[target_feature]
initially.
that was 2 years ago though so maybe that should be re-evaluated. Do you have concerns with allowing #[target_feature]
now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know what a reason for forbidding it would be, and why @joshtriplett thought it would be hard to get right
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well a reason to be cautious is that I think to most (me included) it's not 100% clear what #[target_feature]
could do. Are we absolutely sure that on no platform, present or future, a target feature would insert additional instructions that violate the assumptions of #[naked]
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I said at the time was that it might take some care to get right. If we take that care, and commit to avoiding any potential issues that might arise (e.g. locking down what a given target_feature
means for a naked function and never inserting any instructions for it), then supporting the combination seems fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the implementation strategy in #128004 I don't think there is any risk of #[target_feature]
inserting instructions. The user's inline assembly block is hoisted to a top-level global asm block, and the function itself becomes effectively an extern fn
.
So I've now added target_feature
to the allow list in a separate commit that can be reverted if there are any concerns.
r? bjorn3 |
@bors r+ |
…ssages, r=bjorn3 `#[naked]`: report incompatible attributes tracking issue: rust-lang#90957 this is a re-implementation of rust-lang#93809 by `@bstrie` which was closed 2 years ago due to inactivity. This PR takes some of the final comments into account, specifically providing a little more context in error messages, and using an allow list to determine which attributes are compatible with `#[naked]`. Notable attributes that are incompatible with `#[naked]` are: * `#[inline]` * `#[track_caller]` * ~~`#[target_feature]`~~ (this is now allowed, see discussion below) * `#[test]`, `#[ignore]`, `#[should_panic]` There may be valid use cases for `#[target_feature]` but for now it is disallowed. The others just directly conflict with what `#[naked]` should do. Naked functions are still important for systems programming, embedded and operating systems, so I'd like to move them forward.
Rollup of 9 pull requests Successful merges: - rust-lang#124941 (Stabilize const `{integer}::from_str_radix` i.e. `const_int_from_str`) - rust-lang#127853 (`#[naked]`: report incompatible attributes) - rust-lang#128210 (rustdoc: change title of search results) - rust-lang#128223 (Refactor complex conditions in `collect_tokens_trailing_token`) - rust-lang#128224 (Remove unnecessary range replacements) - rust-lang#128226 (Remove redundant option that was just encoding that a slice was empty) - rust-lang#128227 (CI: do not respect custom try jobs for unrolled perf builds) - rust-lang#128229 (Improve `extern "<abi>" unsafe fn()` error message) - rust-lang#128235 (Fix `Iterator::filter` docs) r? `@ghost` `@rustbot` modify labels: rollup
@bors r- It looks like the changes here are breaking the compiler_builtins build #128248 (comment). It seems like link-related attributes ( |
right, that's a (perma-unstable, apparently) unstable attribute and only |
@rustbot ready though are there other unstable attributes that may be missing from that list? is there a comprehensive list of such attributes? |
I don't know of any list specific for attributes unfortunately, just the huge list in Does this correctly flag on |
…bjorn3 make `///` doc comments compatible with naked functions tracking issue: rust-lang/rust#90957 reported in rust-lang/rust#127853 (comment) it turns out `/// doc comment` and `#[doc = "doc comment"]` are represented differently, at least at the point where we perform the check for what should be allowed. The `///` style doc comment is now also allowed. r? ``@bjorn3`` cc ``@hsanzg``
Before this PR was merged, I was able to use |
The idea of the linked PR is to implement naked functions like this core::arch::global_asm!(".globl page_fault_handler","page_fault_handler:", "ud2");
extern "x86-interrupt" {
fn page_fault_handler(_: u64, _: u64);
} but with support for (const) generics and with being able to export the symbol (which is tricky with extern function declarations in user space). I think in that world, the However, in the meantime, we probably should not break anyone? @bjorn3 do you have thoughts on what to do? |
@Freax13 could you open a new bug for this crash? It seems like that very specific combination of |
codegen `#[naked]` functions using global asm tracking issue: rust-lang#90957 Fixes rust-lang#124375 This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of `#[naked]` functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc). I discussed this approach with `@Amanieu` and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about. Combined with rust-lang#127853, if both accepted, I think that resolves all steps from the tracking issue. r? `@Amanieu`
codegen `#[naked]` functions using global asm tracking issue: rust-lang#90957 Fixes rust-lang#124375 This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of `#[naked]` functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc). I discussed this approach with `@Amanieu` and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about. Combined with rust-lang#127853, if both accepted, I think that resolves all steps from the tracking issue. r? `@Amanieu`
codegen `#[naked]` functions using global asm tracking issue: rust-lang#90957 Fixes rust-lang#124375 This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of `#[naked]` functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc). I discussed this approach with ``@Amanieu`` and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about. Combined with rust-lang#127853, if both accepted, I think that resolves all steps from the tracking issue. r? ``@Amanieu``
codegen `#[naked]` functions using global asm tracking issue: rust-lang#90957 Fixes rust-lang#124375 This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of `#[naked]` functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc). I discussed this approach with `@Amanieu` and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about. Combined with rust-lang#127853, if both accepted, I think that resolves all steps from the tracking issue. r? `@Amanieu`
codegen `#[naked]` functions using global asm tracking issue: rust-lang#90957 Fixes rust-lang#124375 This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of `#[naked]` functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc). I discussed this approach with `@Amanieu` and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about. Combined with rust-lang#127853, if both accepted, I think that resolves all steps from the tracking issue. r? `@Amanieu` try-job: x86_64-gnu-nopt
codegen `#[naked]` functions using global asm tracking issue: rust-lang#90957 Fixes rust-lang#124375 This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of `#[naked]` functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc). I discussed this approach with `@Amanieu` and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about. Combined with rust-lang#127853, if both accepted, I think that resolves all steps from the tracking issue. r? `@Amanieu` try-job: x86_64-gnu-nopt
codegen `#[naked]` functions using global asm tracking issue: rust-lang#90957 Fixes rust-lang#124375 This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of `#[naked]` functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc). I discussed this approach with ``@Amanieu`` and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about. Combined with rust-lang#127853, if both accepted, I think that resolves all steps from the tracking issue. r? ``@Amanieu`` try-job: x86_64-gnu-nopt
codegen `#[naked]` functions using global asm tracking issue: rust-lang#90957 Fixes rust-lang#124375 This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of `#[naked]` functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc). I discussed this approach with `@Amanieu` and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about. Combined with rust-lang#127853, if both accepted, I think that resolves all steps from the tracking issue. r? `@Amanieu` try-job: x86_64-gnu-nopt try-job: x86_64-apple-1
codegen `#[naked]` functions using global asm tracking issue: rust-lang#90957 Fixes rust-lang#124375 This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of `#[naked]` functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc). I discussed this approach with `@Amanieu` and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about. Combined with rust-lang#127853, if both accepted, I think that resolves all steps from the tracking issue. r? `@Amanieu` try-job: x86_64-gnu-nopt try-job: x86_64-apple-1
codegen `#[naked]` functions using global asm tracking issue: rust-lang#90957 Fixes rust-lang#124375 This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of `#[naked]` functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc). I discussed this approach with `@Amanieu` and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about. Combined with rust-lang#127853, if both accepted, I think that resolves all steps from the tracking issue. r? `@Amanieu` try-job: x86_64-gnu-nopt try-job: x86_64-apple-1 try-job: x86-64-msvc try-job: i686-mingw
codegen `#[naked]` functions using global asm tracking issue: rust-lang#90957 Fixes rust-lang#124375 This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of `#[naked]` functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc). I discussed this approach with `@Amanieu` and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about. Combined with rust-lang#127853, if both accepted, I think that resolves all steps from the tracking issue. r? `@Amanieu` try-job: x86_64-gnu-nopt try-job: x86_64-apple-1 try-job: x86_64-msvc try-job: i686-mingw
codegen `#[naked]` functions using global asm tracking issue: rust-lang#90957 Fixes rust-lang#124375 This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of `#[naked]` functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc). I discussed this approach with `@Amanieu` and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about. Combined with rust-lang#127853, if both accepted, I think that resolves all steps from the tracking issue. r? `@Amanieu` try-job: x86_64-gnu-nopt try-job: x86_64-apple-1 try-job: x86_64-msvc try-job: i686-mingw
codegen `#[naked]` functions using global asm tracking issue: rust-lang#90957 Fixes rust-lang#124375 This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of `#[naked]` functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc). I discussed this approach with `@Amanieu` and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about. Combined with rust-lang#127853, if both accepted, I think that resolves all steps from the tracking issue. r? `@Amanieu`
codegen `#[naked]` functions using global asm tracking issue: rust-lang#90957 Fixes rust-lang#124375 This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of `#[naked]` functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc). I discussed this approach with `@Amanieu` and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about. Combined with rust-lang#127853, if both accepted, I think that resolves all steps from the tracking issue. r? `@Amanieu`
codegen `#[naked]` functions using global asm tracking issue: rust-lang#90957 Fixes rust-lang#124375 This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of `#[naked]` functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc). I discussed this approach with `@Amanieu` and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about. Combined with rust-lang#127853, if both accepted, I think that resolves all steps from the tracking issue. r? `@Amanieu`
codegen `#[naked]` functions using global asm tracking issue: rust-lang/rust#90957 Fixes #124375 This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of `#[naked]` functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc). I discussed this approach with `@Amanieu` and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about. Combined with rust-lang/rust#127853, if both accepted, I think that resolves all steps from the tracking issue. r? `@Amanieu`
codegen `#[naked]` functions using global asm tracking issue: rust-lang/rust#90957 Fixes #124375 This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of `#[naked]` functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc). I discussed this approach with `@Amanieu` and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about. Combined with rust-lang/rust#127853, if both accepted, I think that resolves all steps from the tracking issue. r? `@Amanieu`
codegen `#[naked]` functions using global asm tracking issue: rust-lang/rust#90957 Fixes #124375 This implements the approach suggested in the tracking issue: use the existing global assembly infrastructure to emit the body of `#[naked]` functions. The main advantage is that we now have full control over what gets generated, and are no longer dependent on LLVM not sneakily messing with our output (inlining, adding extra instructions, etc). I discussed this approach with `@Amanieu` and while I think the general direction is correct, there is probably a bunch of stuff that needs to change or move around here. I'll leave some inline comments on things that I'm not sure about. Combined with rust-lang/rust#127853, if both accepted, I think that resolves all steps from the tracking issue. r? `@Amanieu`
…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
…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
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
…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
tracking issue: #90957
this is a re-implementation of #93809 by @bstrie which was closed 2 years ago due to inactivity.
This PR takes some of the final comments into account, specifically providing a little more context in error messages, and using an allow list to determine which attributes are compatible with
#[naked]
.Notable attributes that are incompatible with
#[naked]
are:#[inline]
#[track_caller]
(this is now allowed, see PR discussion)#[target_feature]
#[test]
,#[ignore]
,#[should_panic]
These attributes just directly conflict with what
#[naked]
should do.Naked functions are still important for systems programming, embedded, and operating systems, so I'd like to move them forward.