codegen #[naked]
functions using global asm by folkertdev · Pull Request #128004 · rust-lang/rust (original) (raw)
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
[ Show hidden characters]({{ revealButtonHref }})
rustbot added S-waiting-on-review
Status: Awaiting review from the assignee but also interested parties.
Relevant to the compiler team, which will review and decide on the PR/issue.
labels
Comment on lines +182 to +216
This was referenced
Jul 21, 2024
bors mentioned this pull request
xobs mentioned this pull request
antoyo pushed a commit to antoyo/rust that referenced this pull request
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
jieyouxu added a commit to jieyouxu/rust that referenced this pull request
…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:
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 or epilogue when generating code for this function. This attribute is analogous to __attribute__((naked))
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 verifies that
pub
,#[no_mangle]
and#[linkage = "..."]
work correctly for naked functions - 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-* tests for incompatible attributes, generating errors around incorrect use of
naked_asm!
, etc
Interaction with other (unstable) features
fn_align
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, filed on 2015-07-10 and accepted on 2016-03-21. Support for this feature was added in 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, 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 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
extern "C" fn foo() {
core::arch::naked_asm!("ret")
}
is emitted as something similar to
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
Various historical notes
noreturn
RFC 2972 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
…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:
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 or epilogue when generating code for this function. This attribute is analogous to __attribute__((naked))
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 verifies that
pub
,#[no_mangle]
and#[linkage = "..."]
work correctly for naked functions - 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-* tests for incompatible attributes, generating errors around incorrect use of
naked_asm!
, etc
Interaction with other (unstable) features
fn_align
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, filed on 2015-07-10 and accepted on 2016-03-21. Support for this feature was added in 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, 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 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
extern "C" fn foo() {
core::arch::naked_asm!("ret")
}
is emitted as something similar to
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
Various historical notes
noreturn
RFC 2972 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
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:
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 or epilogue when generating code for this function. This attribute is analogous to __attribute__((naked))
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 verifies that
pub
,#[no_mangle]
and#[linkage = "..."]
work correctly for naked functions - 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-* tests for incompatible attributes, generating errors around incorrect use of
naked_asm!
, etc
Interaction with other (unstable) features
fn_align
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, filed on 2015-07-10 and accepted on 2016-03-21. Support for this feature was added in 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, 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 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
extern "C" fn foo() {
core::arch::naked_asm!("ret")
}
is emitted as something similar to
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
Various historical notes
noreturn
RFC 2972 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
…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:
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 or epilogue when generating code for this function. This attribute is analogous to __attribute__((naked))
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 verifies that
pub
,#[no_mangle]
and#[linkage = "..."]
work correctly for naked functions - 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-* tests for incompatible attributes, generating errors around incorrect use of
naked_asm!
, etc
Interaction with other (unstable) features
fn_align
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, filed on 2015-07-10 and accepted on 2016-03-21. Support for this feature was added in #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, 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 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
extern "C" fn foo() {
core::arch::naked_asm!("ret")
}
is emitted as something similar to
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
Various historical notes
noreturn
RFC 2972 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
…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:
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 or epilogue when generating code for this function. This attribute is analogous to __attribute__((naked))
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 verifies that
pub
,#[no_mangle]
and#[linkage = "..."]
work correctly for naked functions - 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-* tests for incompatible attributes, generating errors around incorrect use of
naked_asm!
, etc
Interaction with other (unstable) features
fn_align
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, filed on 2015-07-10 and accepted on 2016-03-21. Support for this feature was added in 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, 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 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
extern "C" fn foo() {
core::arch::naked_asm!("ret")
}
is emitted as something similar to
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
Various historical notes
noreturn
RFC 2972 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
…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:
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 or epilogue when generating code for this function. This attribute is analogous to __attribute__((naked))
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 verifies that
pub
,#[no_mangle]
and#[linkage = "..."]
work correctly for naked functions - 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-* tests for incompatible attributes, generating errors around incorrect use of
naked_asm!
, etc
Interaction with other (unstable) features
fn_align
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, filed on 2015-07-10 and accepted on 2016-03-21. Support for this feature was added in #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, 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 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
extern "C" fn foo() {
core::arch::naked_asm!("ret")
}
is emitted as something similar to
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
Various historical notes
noreturn
RFC 2972 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
tgross35 added a commit to tgross35/rust that referenced this pull request
…esleywiser
Share the naked asm impl between cg_ssa and cg_clif
This was introduced in rust-lang#128004.
bors added a commit to rust-lang-ci/rust that referenced this pull request
Share the naked asm impl between cg_ssa and cg_clif
This was introduced in rust-lang#128004.
try-job: x86_64-apple-1
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request
…esleywiser
Share the naked asm impl between cg_ssa and cg_clif
This was introduced in rust-lang#128004.
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request
Rollup merge of rust-lang#134232 - bjorn3:naked_asm_improvements, r=wesleywiser
Share the naked asm impl between cg_ssa and cg_clif
This was introduced in rust-lang#128004.
bjorn3 pushed a commit to rust-lang/rustc_codegen_cranelift that referenced this pull request
antoyo pushed a commit to rust-lang/rustc_codegen_gcc that referenced this pull request