Skip to content

naked functions #1689

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 20 commits into from
Apr 22, 2025
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ The following attributes are unsafe:
* [`export_name`]
* [`link_section`]
* [`no_mangle`]
* [`naked`]

r[attributes.kind]
Attributes can be classified into the following kinds:
Expand Down Expand Up @@ -290,6 +291,7 @@ The following is an index of all built-in attributes.
- Code generation
- [`inline`] --- Hint to inline code.
- [`cold`] --- Hint that a function is unlikely to be called.
- [`naked`] - Prevent the compiler from emitting a function prologue.
- [`no_builtins`] --- Disables use of certain built-in functions.
- [`target_feature`] --- Configure platform-specific code generation.
- [`track_caller`] --- Pass the parent call location to `std::panic::Location::caller()`.
Expand Down Expand Up @@ -360,6 +362,7 @@ The following is an index of all built-in attributes.
[`macro_export`]: macros-by-example.md#path-based-scope
[`macro_use`]: macros-by-example.md#the-macro_use-attribute
[`must_use`]: attributes/diagnostics.md#the-must_use-attribute
[`naked`]: attributes/codegen.md#the-naked-attribute
[`no_builtins`]: attributes/codegen.md#the-no_builtins-attribute
[`no_implicit_prelude`]: names/preludes.md#the-no_implicit_prelude-attribute
[`no_link`]: items/extern-crates.md#the-no_link-attribute
Expand Down
41 changes: 41 additions & 0 deletions src/attributes/codegen.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,39 @@ r[attributes.codegen.cold]
The *`cold` [attribute]* suggests that the attributed function is unlikely to
be called.

r[attributes.codegen.naked]
## The `naked` attribute

r[attributes.codegen.naked.intro]
The *`naked` [attribute]* prevents the compiler from emitting a function prologue and epilogue for the attributed function.

r[attributes.codegen.naked.body]
The [function body] must consist of exactly one [`naked_asm!`] macro invocation.

r[attributes.codegen.naked.prologue-epilogue]
No function prologue or epilogue are generated for the attributed function: the contents of the `naked_asm!` invocation make up the full body of a naked function.

r[attributes.codegen.naked.unsafe-attribute]
The `naked` attribute is an [unsafe attribute]. Annotating a function with `#[unsafe(naked)]` comes with the safety obligation that the body respects the function's calling convention, and that the body either returns or diverges.

r[attributes.codegen.naked.call-stack]
The assembly code will have a valid call stack and register state on entry as per the signature and calling convention of the function.

r[attributes.codegen.naked.no-duplication]
The assembly code may not be duplicated by the compiler, except when monomorphizing polymorphic functions. This property is important for naked functions that define symbols in the assembly code.

r[attributes.codegen.naked.unused-variables]
The [`unused_variables`] lint is suppressed within naked functions.

r[attributes.codegen.naked.inline]
A naked function cannot be attributed by the [`inline`](#the-inline-attribute) attribute.

r[attributes.codegen.naked.track_caller]
A naked function cannot be attributed by the [`track_caller`](#the-track_caller-attribute) attribute.

r[attributes.codegen.naked.testing]
A naked function cannot be attributed by [the testing attributes](testing.md).

r[attributes.codegen.no_builtins]
## The `no_builtins` attribute

Expand Down Expand Up @@ -499,13 +532,21 @@ trait object whose methods are attributed.
[`-C target-feature`]: ../../rustc/codegen-options/index.html#target-feature
[`is_x86_feature_detected`]: ../../std/arch/macro.is_x86_feature_detected.html
[`is_aarch64_feature_detected`]: ../../std/arch/macro.is_aarch64_feature_detected.html
[`naked_asm!`]: ../inline-assembly.md
[`inline`]: #the-inline-attribute
[`track_caller`]: #the-track-caller-attribute
[`target_feature` conditional compilation option]: ../conditional-compilation.md#target_feature
[`unused_variables`]: ../../rustc/lints/listing/warn-by-default.html#unused-variables
[attribute]: ../attributes.md
[attributes]: ../attributes.md
[FFI-safe]: ../../rustc/lints/listing/warn-by-default.html#improper-ctypes-definitions
[function body]: ../items/functions.md#function-body
[functions]: ../items/functions.md
[rules for inline assembly]: ../inline-assembly.md#rules-for-inline-assembly
[target architecture]: ../conditional-compilation.md#target_arch
[trait]: ../items/traits.md
[undefined behavior]: ../behavior-considered-undefined.md
[unsafe attribute]: ../attributes.md#r-attributes.safety
[rust-abi]: ../items/external-blocks.md#abi
[`Location`]: core::panic::Location

Expand Down
91 changes: 87 additions & 4 deletions src/inline-assembly.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ r[asm]
# Inline assembly

r[asm.intro]
Support for inline assembly is provided via the [`asm!`] and [`global_asm!`] macros.
Support for inline assembly is provided via the [`asm!`], [`naked_asm!`], and [`global_asm!`] macros.
It can be used to embed handwritten assembly in the assembly output generated by the compiler.

[`asm!`]: core::arch::asm
[`naked_asm!`]: core::arch::naked_asm
[`global_asm!`]: core::arch::global_asm

r[asm.stable-targets]
Expand Down Expand Up @@ -58,14 +59,15 @@ option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "nost
options := "options(" option *("," option) [","] ")"
operand := reg_operand / clobber_abi / options
asm := "asm!(" format_string *("," format_string) *("," operand) [","] ")"
naked_asm := "naked_asm!(" format_string *("," format_string) *("," operand) [","] ")"
global_asm := "global_asm!(" format_string *("," format_string) *("," operand) [","] ")"
```

r[asm.scope]
## Scope

r[asm.scope.intro]
Inline assembly can be used in one of two ways.
Inline assembly can be used in one of three ways.

r[asm.scope.asm]
With the `asm!` macro, the assembly code is emitted in a function scope and integrated into the compiler-generated assembly code of a function.
Expand All @@ -78,6 +80,19 @@ unsafe { core::arch::asm!("/* {} */", in(reg) 0); }
# }
```

r[asm.scope.naked_asm]
With the `naked_asm!` macro, the assembly code is emitted in a function scope and constitutes the full assembly code of a function.
The `naked_asm!` macro is only allowed in [naked functions](attributes/codegen.md#the-naked-attribute).

```rust
# #[cfg(target_arch = "x86_64")] {
# #[unsafe(naked)]
# extern "C" fn wrapper() {
core::arch::naked_asm!("/* {} */", const 0);
# }
# }
```

r[asm.scope.global_asm]
With the `global_asm!` macro, the assembly code is emitted in a global scope, outside a function.
This can be used to hand-write entire functions using assembly code, and generally provides much more freedom to use arbitrary registers and assembler directives.
Expand Down Expand Up @@ -384,8 +399,11 @@ assert_eq!(y, 1);
# }
```

r[asm.operand-type.naked_asm-restriction]
Because `naked_asm!` defines a whole function body and the compiler cannot emit any additional code to handle operands, it can only use `sym` and `const` operands.

r[asm.operand-type.global_asm-restriction]
Since `global_asm!` exists outside a function, it can only use `sym` and `const` operands.
Because `global_asm!` exists outside a function, it can only use `sym` and `const` operands.

```rust,compile_fail
# fn main() {}
Expand Down Expand Up @@ -1206,9 +1224,13 @@ unsafe { core::arch::asm!("mov {:e}, 1", out(reg) z, options(noreturn)); }
# #[cfg(not(target_arch = "x86_64"))] core::compile_error!("Test not supported on this arch");
```

r[asm.options.naked_asm-restriction]
`naked_asm!` only supports the `att_syntax` and `raw` options.
The remaining options are not meaningful because the inline assembly defines the whole function body.

r[asm.options.global_asm-restriction]
`global_asm!` only supports the `att_syntax` and `raw` options.
The remaining options are not meaningful for global-scope inline assembly
The remaining options are not meaningful for global-scope inline assembly.

```rust,compile_fail
# fn main() {}
Expand Down Expand Up @@ -1362,6 +1384,67 @@ r[asm.rules.preserves_flags]
> [!NOTE]
> As a general rule, the flags covered by `preserves_flags` are those which are *not* preserved when performing a function call.

r[asm.naked-rules]
## Rules for naked inline assembly

r[asm.naked-rules.intro]
To avoid undefined behavior, these rules must be followed when using function-scope inline assembly in naked functions (`naked_asm!`):

r[asm.naked-rules.reg-not-input]
- Any registers not used for function inputs according to the calling convention and function signature will contain an undefined value on entry to the `naked_asm!` block.
- An "undefined value" in the context of inline assembly means that the register can (non-deterministically) have any one of the possible values allowed by the architecture.
Notably it is not the same as an LLVM `undef` which can have a different value every time you read it (since such a concept does not exist in assembly code).

r[asm.naked-rules.reg-not-output]
- Any callee-saved registers must have the same value upon return as they had on entry, otherwise behavior is undefined.
- Caller-saved registes may be used freely, even if they are not used for the return value.

r[asm.naked-rules.noreturn]
- Behavior is undefined if execution falls through to the end of the `naked_asm!` block.
- every path through the assembly code is expected to terminate with a return instruction or to diverge

r[asm.naked-rules.mem-same-as-ffi]
- The set of memory locations that assembly code is allowed to read and write are the same as those allowed for an FFI function.
- Refer to the unsafe code guidelines for the exact rules.
- These rules do not apply to memory which is private to the assembly code, such as stack space allocated within the `naked_asm!` block.

r[asm.naked-rules.black-box]
- The compiler cannot assume that the instructions in the `naked_asm!` block are the ones that will actually be executed.
- This effectively means that the compiler must treat the `naked_asm!` as a black box and only take the interface specification into account, not the instructions themselves.
- Runtime code patching is allowed, via target-specific mechanisms.

r[asm.naked-rules.unwind]
- Unwinding out of a `naked_asm!` block is allowed.
- For correct behavior, the appropriate assembler directives that emit unwinding metadata must be used.

```rust
# #[cfg(target_arch = "x86_64")] {
#[unsafe(naked)]
extern "C-unwind" fn naked_function() {
core::arch::naked_asm!(
".cfi_startproc",
"push rbp",
".cfi_def_cfa_offset 16",
".cfi_offset rbp, -16",
"mov rbp, rsp",
".cfi_def_cfa_register rbp",
"",
"call {function}",
"",
"pop rbp",
".cfi_def_cfa rsp, 8",
"ret",
".cfi_endproc",
function = sym function_that_panics,
)
}

extern "C-unwind" fn function_that_panics() {
panic!("unwind!");
}
# }
```

r[asm.validity]
### Correctness and Validity

Expand Down
Loading