From 4ee56d4874474a076e9fb3ffb45b3ebc15d52f68 Mon Sep 17 00:00:00 2001 From: bstrie <865233+bstrie@users.noreply.github.com> Date: Mon, 31 Jan 2022 16:08:11 -0500 Subject: [PATCH 01/20] Add documentation for the `naked` function attribute --- src/attributes/codegen.md | 86 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/src/attributes/codegen.md b/src/attributes/codegen.md index 4f9be65bc..1b03eb96d 100644 --- a/src/attributes/codegen.md +++ b/src/attributes/codegen.md @@ -46,6 +46,81 @@ r[attributes.codegen.cold] The *`cold` [attribute]* suggests that the attributed function is unlikely to be called. +r[attributes.codegen.naked] +## The `naked` attribute + +The *`naked` [attribute]* may be applied to a function in order to prevent the compiler +from emitting a function prologue. + +### Requirements + +Any function marked with the `naked` attribute must meet the following requirements; +failure to do so will result in a compiler error. + +* The [function body] must consist of exactly one [`asm!`] macro invocation, + which may be enclosed within an [unsafe block]. + * This `asm!` invocation must not contain any [operands] + except for `const` and `sym` operands. + * This `asm!` invocation must specify the `noreturn` [option], + and must not specify any other options except for `att_syntax`. +* The function must not be marked with the [`inline`] attribute. + +### Recommendations + +Any function marked with the `naked` attribute should adhere to the following recommendations; +failure to do so will result in a compiler warning. + +* The function should feature an [extern function qualifier] that is not `extern "Rust"`. +* All arguments and return types of the function should be [FFI-safe]. + +### Effects + +Marking a function with the `naked` attribute has the following effects: + +* The compiler will not generate a prologue for this function. + Within the function, all registers will remain precisely as they were set up + by its caller. +* The compiler will suppress the [`unused_variables`] lint for this function. + +### Notes + +* The [rules for inline assembly] ordinarily consider it undefined behavior to + refer to registers not specified as input operands, or to modify + registers not specified as output operands. + The reason for this is because ordinarily an `asm!` invocation cannot guarantee + the state of the registers surrounding the assembly block. + However, in naked functions the state of the registers is guaranteed + by adherence to the specified calling convention. + Therefore, it is not undefined behavior for the `asm!` invocation in a naked function + to refer to registers without specifying them as operands. +* A naked function that makes use of registers in a way that does not conform + to the specified calling convention imposes additional safety invariants on its caller, + and therefore must be marked as an [unsafe function]. +* Implementations may assume that naked functions never unwind. + Unwinding through a naked function is undefined behavior. +* The semantics of naked functions require implementations to set up the call stack + according to the specified calling convention before executing a naked function, + even in contexts where setting up the call stack would ordinarily be unnecessary, + such as when the function is inlined. + An implementation can fulfill this requirement by guaranteeing that naked functions + are never inlined. + However, implementations are not currently required to guarantee that naked functions + are never inlined. + In the future it may become a requirement for implementations to guarantee that + naked functions are never inlined; + users must not rely on any observable behavior that may result from inlining. +* Although implementations are prohibited from generating code for a naked function that + contains any instructions that precede the naked function's `asm!` block, + under some circumstances, implementations may generate code that contains instructions + *after* a naked function's `asm!` block. + In the future it may become a requirement for implementations to guarantee + the absence of any instructions following a naked function's `asm!` block; + users must not rely on the presence of any trailing instructions. + If a user of the `naked` attribute relies on the absence of trailing instructions + for correctness, for the time being it is the user's responsibility to ensure that + the instructions truly are absent, + for example by passing any necessary code generation flags to the compiler. + r[attributes.codegen.no_builtins] ## The `no_builtins` attribute @@ -499,13 +574,24 @@ 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 [`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 +[extern function qualifier]: ../items/functions.md#extern-function-qualifier +[FFI-safe]: ../../rustc/lints/listing/warn-by-default.html#improper-ctypes-definitions +[function body]: ../items/functions.md#function-body [functions]: ../items/functions.md +[operands]: ../inline-assembly.md#operand-type +[option]: ../inline-assembly.md#options +[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 block]: ../unsafe-blocks.md +[unsafe function]: ../unsafe-functions.md [rust-abi]: ../items/external-blocks.md#abi [`Location`]: core::panic::Location From 69d5534663f453310f75d497f16ddbc03af50854 Mon Sep 17 00:00:00 2001 From: bstrie <865233+bstrie@users.noreply.github.com> Date: Wed, 2 Feb 2022 16:05:07 -0500 Subject: [PATCH 02/20] Add `naked` to the attribute index --- src/attributes.md | 2 + src/attributes/codegen.md | 115 +++++++++++++++----------------------- 2 files changed, 46 insertions(+), 71 deletions(-) diff --git a/src/attributes.md b/src/attributes.md index e5ca10879..f8ae91dde 100644 --- a/src/attributes.md +++ b/src/attributes.md @@ -290,6 +290,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()`. @@ -360,6 +361,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 diff --git a/src/attributes/codegen.md b/src/attributes/codegen.md index 1b03eb96d..3969b49ae 100644 --- a/src/attributes/codegen.md +++ b/src/attributes/codegen.md @@ -49,77 +49,49 @@ be called. r[attributes.codegen.naked] ## The `naked` attribute -The *`naked` [attribute]* may be applied to a function in order to prevent the compiler -from emitting a function prologue. - -### Requirements - -Any function marked with the `naked` attribute must meet the following requirements; -failure to do so will result in a compiler error. - -* The [function body] must consist of exactly one [`asm!`] macro invocation, - which may be enclosed within an [unsafe block]. - * This `asm!` invocation must not contain any [operands] - except for `const` and `sym` operands. - * This `asm!` invocation must specify the `noreturn` [option], - and must not specify any other options except for `att_syntax`. -* The function must not be marked with the [`inline`] attribute. - -### Recommendations - -Any function marked with the `naked` attribute should adhere to the following recommendations; -failure to do so will result in a compiler warning. - -* The function should feature an [extern function qualifier] that is not `extern "Rust"`. -* All arguments and return types of the function should be [FFI-safe]. - -### Effects - -Marking a function with the `naked` attribute has the following effects: - -* The compiler will not generate a prologue for this function. - Within the function, all registers will remain precisely as they were set up - by its caller. -* The compiler will suppress the [`unused_variables`] lint for this function. - -### Notes - -* The [rules for inline assembly] ordinarily consider it undefined behavior to - refer to registers not specified as input operands, or to modify - registers not specified as output operands. - The reason for this is because ordinarily an `asm!` invocation cannot guarantee - the state of the registers surrounding the assembly block. - However, in naked functions the state of the registers is guaranteed - by adherence to the specified calling convention. - Therefore, it is not undefined behavior for the `asm!` invocation in a naked function - to refer to registers without specifying them as operands. -* A naked function that makes use of registers in a way that does not conform - to the specified calling convention imposes additional safety invariants on its caller, - and therefore must be marked as an [unsafe function]. -* Implementations may assume that naked functions never unwind. - Unwinding through a naked function is undefined behavior. -* The semantics of naked functions require implementations to set up the call stack - according to the specified calling convention before executing a naked function, - even in contexts where setting up the call stack would ordinarily be unnecessary, - such as when the function is inlined. - An implementation can fulfill this requirement by guaranteeing that naked functions - are never inlined. - However, implementations are not currently required to guarantee that naked functions - are never inlined. - In the future it may become a requirement for implementations to guarantee that - naked functions are never inlined; - users must not rely on any observable behavior that may result from inlining. -* Although implementations are prohibited from generating code for a naked function that - contains any instructions that precede the naked function's `asm!` block, - under some circumstances, implementations may generate code that contains instructions - *after* a naked function's `asm!` block. - In the future it may become a requirement for implementations to guarantee - the absence of any instructions following a naked function's `asm!` block; - users must not rely on the presence of any trailing instructions. - If a user of the `naked` attribute relies on the absence of trailing instructions - for correctness, for the time being it is the user's responsibility to ensure that - the instructions truly are absent, - for example by passing any necessary code generation flags to the compiler. +r[attributes.codegen.naked] + +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, which +may be enclosed within an [unsafe block]. + +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.call-stack] +The caller must set up the call stack acording to the specified calling convention before +executing a naked function, even in contexts where setting up the call stack would ordinarily +be unnecessary, such as when the function is inlined. + +An implementation can fulfill this requirement by guaranteeing that naked functions +are never inlined. However, implementations are not currently required to guarantee that +naked functions are never inlined. + +In the future it may become a requirement for implementations to guarantee that +naked functions are never inlined; users must not rely on any observable behavior +that may result from inlining. according to the specified calling convention before +executing a naked function, + +r[attributes.codegen.naked.unsafe-function] +A naked function that makes use of registers in a way that does not conform +to the specified calling convention imposes additional safety invariants on its caller, +and therefore must be marked as an [unsafe function]. + +> ***Note***: a `naked_asm!` invocation may refer to registers that were not specified as operands. +> for standard `asm!` this is undefined behavior, but `inline_asm!` may rely on the state of registers +> as specified by the calling convention. + +r[attributes.codegen.naked.unused-variables] +The [`unused_variables`] lint is suppressed within naked functions. + +r[attributes.codegen.naked.no-unwind] +Implementations may assume that naked functions never unwind. +Unwinding through a naked function is undefined behavior. r[attributes.codegen.no_builtins] ## The `no_builtins` attribute @@ -576,6 +548,7 @@ trait object whose methods are attributed. [`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 From e42068debed5762d0fbca2c12873b10f82300544 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 1 Dec 2024 16:19:30 +0100 Subject: [PATCH 03/20] document `naked_asm!` --- src/attributes/codegen.md | 4 +-- src/inline-assembly.md | 58 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/attributes/codegen.md b/src/attributes/codegen.md index 3969b49ae..0aeabdd53 100644 --- a/src/attributes/codegen.md +++ b/src/attributes/codegen.md @@ -64,7 +64,7 @@ No function prologue or epilogue are generated for the attributed function: the of the `naked_asm!` invocation make up the full body of a naked function. r[attributes.codegen.naked.call-stack] -The caller must set up the call stack acording to the specified calling convention before +The caller must set up the call stack according to the specified calling convention before executing a naked function, even in contexts where setting up the call stack would ordinarily be unnecessary, such as when the function is inlined. @@ -83,7 +83,7 @@ to the specified calling convention imposes additional safety invariants on its and therefore must be marked as an [unsafe function]. > ***Note***: a `naked_asm!` invocation may refer to registers that were not specified as operands. -> for standard `asm!` this is undefined behavior, but `inline_asm!` may rely on the state of registers +> for standard `asm!` this is undefined behavior, but `naked_asm!` may rely on the state of registers > as specified by the calling convention. r[attributes.codegen.naked.unused-variables] diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 094d2f43e..2605b28dd 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -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] @@ -58,6 +59,7 @@ 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 := "asm!(" format_string *("," format_string) *("," operand) [","] ")" global_asm := "global_asm!(" format_string *("," format_string) *("," operand) [","] ")" ``` @@ -65,7 +67,7 @@ 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. @@ -78,6 +80,10 @@ 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). + 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. @@ -384,8 +390,11 @@ assert_eq!(y, 1); # } ``` +r[asm.operand-type.naked_asm-restriction] +Because `naked_asm!` defines a whole function body, 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() {} @@ -1206,9 +1215,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] +`global_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() {} @@ -1362,6 +1375,43 @@ 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 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 exiting the asm block 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.unwind] +- Behavior is undefined if execution unwinds out of an asm block. + - This also applies if the assembly code calls a function which then unwinds. + +r[asm.naked-rules.noreturn] +- Behavior is undefined if execution falls through to the end of the asm block. + +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 asm code, such as stack space allocated within the asm block. + +r[asm.naked-rules.black-box] +- The compiler cannot assume that the instructions in the asm are the ones that will actually end up 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. + - However there is no guarantee that each `naked_asm!` directly corresponds to a single instance of instructions in the object file: the compiler is free to duplicate or deduplicate `naked_asm!` blocks. + +r[asm.naked-rules.not-exactly-once] +- You cannot assume that an `naked_asm!` block will appear exactly once in the output binary. + The compiler is allowed to instantiate multiple copies of the `naked_asm!` block, for example when the function containing it is inlined in multiple places. + r[asm.validity] ### Correctness and Validity From 04d5b88eb4aeea08ae4fc53c4032cb19c7cd26b9 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 1 Dec 2024 16:44:09 +0100 Subject: [PATCH 04/20] mention incompatible attributes --- src/attributes/codegen.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/attributes/codegen.md b/src/attributes/codegen.md index 0aeabdd53..e80e9d847 100644 --- a/src/attributes/codegen.md +++ b/src/attributes/codegen.md @@ -82,16 +82,17 @@ A naked function that makes use of registers in a way that does not conform to the specified calling convention imposes additional safety invariants on its caller, and therefore must be marked as an [unsafe function]. -> ***Note***: a `naked_asm!` invocation may refer to registers that were not specified as operands. -> for standard `asm!` this is undefined behavior, but `naked_asm!` may rely on the state of registers -> as specified by the calling convention. - r[attributes.codegen.naked.unused-variables] The [`unused_variables`] lint is suppressed within naked functions. -r[attributes.codegen.naked.no-unwind] -Implementations may assume that naked functions never unwind. -Unwinding through a naked function is undefined behavior. +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 From f64c8ca465de2a64285b6253b7d57ecc19f788a8 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 1 Dec 2024 16:54:55 +0100 Subject: [PATCH 05/20] remove unused links --- src/attributes/codegen.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/attributes/codegen.md b/src/attributes/codegen.md index e80e9d847..156eba607 100644 --- a/src/attributes/codegen.md +++ b/src/attributes/codegen.md @@ -554,12 +554,9 @@ trait object whose methods are attributed. [`unused_variables`]: ../../rustc/lints/listing/warn-by-default.html#unused-variables [attribute]: ../attributes.md [attributes]: ../attributes.md -[extern function qualifier]: ../items/functions.md#extern-function-qualifier [FFI-safe]: ../../rustc/lints/listing/warn-by-default.html#improper-ctypes-definitions [function body]: ../items/functions.md#function-body [functions]: ../items/functions.md -[operands]: ../inline-assembly.md#operand-type -[option]: ../inline-assembly.md#options [rules for inline assembly]: ../inline-assembly.md#rules-for-inline-assembly [target architecture]: ../conditional-compilation.md#target_arch [trait]: ../items/traits.md From 2401d21dd67f29bcaa9895d3ae18f8a40da7ca03 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 1 Dec 2024 19:31:27 +0100 Subject: [PATCH 06/20] changes after code review also maybe make links work? --- src/attributes/codegen.md | 17 +++++------------ src/inline-assembly.md | 11 ++++------- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/src/attributes/codegen.md b/src/attributes/codegen.md index 156eba607..99598a98e 100644 --- a/src/attributes/codegen.md +++ b/src/attributes/codegen.md @@ -64,18 +64,11 @@ No function prologue or epilogue are generated for the attributed function: the of the `naked_asm!` invocation make up the full body of a naked function. r[attributes.codegen.naked.call-stack] -The caller must set up the call stack according to the specified calling convention before -executing a naked function, even in contexts where setting up the call stack would ordinarily -be unnecessary, such as when the function is inlined. +The asm code will have a valid call stack and register state on entry as per the signature and calling convention of the function. -An implementation can fulfill this requirement by guaranteeing that naked functions -are never inlined. However, implementations are not currently required to guarantee that -naked functions are never inlined. - -In the future it may become a requirement for implementations to guarantee that -naked functions are never inlined; users must not rely on any observable behavior -that may result from inlining. according to the specified calling convention before -executing a naked function, +r[attributes.codegen.naked.no-duplication] +The asm code may not be duplicated by the compiler. +This property is important for naked functions that define symbols in the assembly code. r[attributes.codegen.naked.unsafe-function] A naked function that makes use of registers in a way that does not conform @@ -92,7 +85,7 @@ 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). +A naked function cannot be attributed by [the testing attributes](attributes/testing.md). r[attributes.codegen.no_builtins] ## The `no_builtins` attribute diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 2605b28dd..be57837c7 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -82,7 +82,7 @@ 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). +The `naked_asm!` macro is only allowed in [naked functions](attributes/codegen.md#the-naked-attribute). r[asm.scope.global_asm] With the `global_asm!` macro, the assembly code is emitted in a global scope, outside a function. @@ -1216,7 +1216,7 @@ unsafe { core::arch::asm!("mov {:e}, 1", out(reg) z, options(noreturn)); } ``` r[asm.options.naked_asm-restriction] -`global_asm!` only supports the `att_syntax` and `raw` options. +`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] @@ -1387,7 +1387,7 @@ r[asm.naked-rules.reg-not-input] 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 exiting the asm block as they had on entry, otherwise behavior is undefined. +- 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.unwind] @@ -1396,6 +1396,7 @@ r[asm.naked-rules.unwind] r[asm.naked-rules.noreturn] - Behavior is undefined if execution falls through to the end of the asm block. + - the assembly code is expected to contain 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. @@ -1408,10 +1409,6 @@ r[asm.naked-rules.black-box] - Runtime code patching is allowed, via target-specific mechanisms. - However there is no guarantee that each `naked_asm!` directly corresponds to a single instance of instructions in the object file: the compiler is free to duplicate or deduplicate `naked_asm!` blocks. -r[asm.naked-rules.not-exactly-once] -- You cannot assume that an `naked_asm!` block will appear exactly once in the output binary. - The compiler is allowed to instantiate multiple copies of the `naked_asm!` block, for example when the function containing it is inlined in multiple places. - r[asm.validity] ### Correctness and Validity From 961ef791ead19e7ecc5474245cf353e0e605075d Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 1 Dec 2024 21:23:26 +0100 Subject: [PATCH 07/20] fix link --- src/attributes/codegen.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/attributes/codegen.md b/src/attributes/codegen.md index 99598a98e..2b04656a5 100644 --- a/src/attributes/codegen.md +++ b/src/attributes/codegen.md @@ -85,7 +85,7 @@ 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](attributes/testing.md). +A naked function cannot be attributed by [the testing attributes](testing.md). r[attributes.codegen.no_builtins] ## The `no_builtins` attribute From 6f74661aed4a32c546af228d36db2329a1e7c8ff Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 2 Dec 2024 10:02:23 +0100 Subject: [PATCH 08/20] add rule on black box/code patching --- src/inline-assembly.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index be57837c7..7fd3bbf97 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -1403,11 +1403,10 @@ r[asm.naked-rules.mem-same-as-ffi] - Refer to the unsafe code guidelines for the exact rules. - These rules do not apply to memory which is private to the asm code, such as stack space allocated within the asm block. -r[asm.naked-rules.black-box] +r[asm.rules.black-box] - The compiler cannot assume that the instructions in the asm are the ones that will actually end up 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. - - However there is no guarantee that each `naked_asm!` directly corresponds to a single instance of instructions in the object file: the compiler is free to duplicate or deduplicate `naked_asm!` blocks. r[asm.validity] ### Correctness and Validity From b17fcf3ebf0280822600fa58e9867ff488ebc87b Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 2 Dec 2024 10:31:56 +0100 Subject: [PATCH 09/20] fix rule name Co-authored-by: bjorn3 <17426603+bjorn3@users.noreply.github.com> --- src/inline-assembly.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 7fd3bbf97..31b2c28a6 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -1403,7 +1403,7 @@ r[asm.naked-rules.mem-same-as-ffi] - Refer to the unsafe code guidelines for the exact rules. - These rules do not apply to memory which is private to the asm code, such as stack space allocated within the asm block. -r[asm.rules.black-box] +r[asm.naked-rules.black-box] - The compiler cannot assume that the instructions in the asm are the ones that will actually end up 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. From 5e43776e7cc1db837839a46024adeb1d1ce13813 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 18 Dec 2024 21:44:49 +0100 Subject: [PATCH 10/20] add note about monomorphization --- src/attributes/codegen.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/attributes/codegen.md b/src/attributes/codegen.md index 2b04656a5..fe39faf02 100644 --- a/src/attributes/codegen.md +++ b/src/attributes/codegen.md @@ -67,7 +67,7 @@ r[attributes.codegen.naked.call-stack] The asm 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 asm code may not be duplicated by the compiler. +The asm 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.unsafe-function] From 189004e18c8e09e52854f7a5856a6c05b53798fb Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 20 Dec 2024 12:28:12 +0100 Subject: [PATCH 11/20] Update src/inline-assembly.md Co-authored-by: Laine Taffin Altman --- src/attributes/codegen.md | 2 -- src/inline-assembly.md | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/attributes/codegen.md b/src/attributes/codegen.md index fe39faf02..ff36461bb 100644 --- a/src/attributes/codegen.md +++ b/src/attributes/codegen.md @@ -49,8 +49,6 @@ be called. r[attributes.codegen.naked] ## The `naked` attribute -r[attributes.codegen.naked] - r[attributes.codegen.naked.intro] The *`naked` [attribute]* prevents the compiler from emitting a function prologue and epilogue for the attributed function. diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 31b2c28a6..96dfa7506 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -59,7 +59,7 @@ 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 := "asm!(" format_string *("," format_string) *("," operand) [","] ")" +naked_asm := "naked_asm!(" format_string *("," format_string) *("," operand) [","] ")" global_asm := "global_asm!(" format_string *("," format_string) *("," operand) [","] ")" ``` From 5928c4bdb3a9a94bcae07b47adcc7af4ac9a3852 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 7 Mar 2025 19:30:20 +0100 Subject: [PATCH 12/20] remove the rule on unwinding. this only applies to `asm!` (even though the rule has been removed there since copying it for naked asm --- src/inline-assembly.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 96dfa7506..9eb6ca50c 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -1390,10 +1390,6 @@ 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.unwind] -- Behavior is undefined if execution unwinds out of an asm block. - - This also applies if the assembly code calls a function which then unwinds. - r[asm.naked-rules.noreturn] - Behavior is undefined if execution falls through to the end of the asm block. - the assembly code is expected to contain a return instruction or to diverge From 1c3e5202de4ecf6950f5b51f16b76114a4734898 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 7 Mar 2025 19:32:01 +0100 Subject: [PATCH 13/20] use `naked_asm!` block to clarify we're talking just about naked assembly --- src/inline-assembly.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 9eb6ca50c..6396d23e8 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -1382,7 +1382,7 @@ 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 asm block. +- 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). @@ -1391,16 +1391,16 @@ r[asm.naked-rules.reg-not-output] - 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 asm block. +- Behavior is undefined if execution falls through to the end of the `naked_asm!` block. - the assembly code is expected to contain 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 asm code, such as stack space allocated within the asm block. + - These rules do not apply to memory which is private to the asm 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 asm are the ones that will actually end up executed. +- 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. From 0f837d842e8687494bbf5f7783cd1f3d85b64d15 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 7 Mar 2025 19:52:57 +0100 Subject: [PATCH 14/20] reintroduce a section on unwinding out of `naked_asm!` --- src/inline-assembly.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 6396d23e8..c770b9f9d 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -1404,6 +1404,41 @@ r[asm.naked-rules.black-box] - 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. + + +```txt +# #[cfg(target_arch = "x86_64")] { +#[naked] +extern "C-unwind" fn naked_function() { + unsafe { + 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 From c4d68c997c84c4e75badec2c668fb83f1439f26e Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 19 Apr 2025 12:39:37 +0200 Subject: [PATCH 15/20] formatting + update to unsafe(naked) --- src/attributes.md | 1 + src/attributes/codegen.md | 25 +++++++---------- src/inline-assembly.md | 56 ++++++++++++++++++++++----------------- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/attributes.md b/src/attributes.md index f8ae91dde..cca010ca2 100644 --- a/src/attributes.md +++ b/src/attributes.md @@ -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: diff --git a/src/attributes/codegen.md b/src/attributes/codegen.md index ff36461bb..5f77ed9e2 100644 --- a/src/attributes/codegen.md +++ b/src/attributes/codegen.md @@ -50,28 +50,22 @@ 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. +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, which -may be enclosed within an [unsafe block]. +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. +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 asm code will have a valid call stack and register state on entry as per the signature and calling convention of the function. +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 asm 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.unsafe-function] -A naked function that makes use of registers in a way that does not conform -to the specified calling convention imposes additional safety invariants on its caller, -and therefore must be marked as an [unsafe function]. +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. @@ -552,8 +546,7 @@ trait object whose methods are attributed. [target architecture]: ../conditional-compilation.md#target_arch [trait]: ../items/traits.md [undefined behavior]: ../behavior-considered-undefined.md -[unsafe block]: ../unsafe-blocks.md -[unsafe function]: ../unsafe-functions.md +[unsafe attribute]: ../attributes.md#r-attributes.safety [rust-abi]: ../items/external-blocks.md#abi [`Location`]: core::panic::Location diff --git a/src/inline-assembly.md b/src/inline-assembly.md index c770b9f9d..0c7049161 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -2,7 +2,7 @@ r[asm] # Inline assembly r[asm.intro] -Support for inline assembly is provided via the [`asm!`], [`naked_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 @@ -84,6 +84,15 @@ 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. @@ -391,7 +400,7 @@ assert_eq!(y, 1); ``` r[asm.operand-type.naked_asm-restriction] -Because `naked_asm!` defines a whole function body, it can only use `sym` and `const` operands. +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] Because `global_asm!` exists outside a function, it can only use `sym` and `const` operands. @@ -1392,12 +1401,12 @@ r[asm.naked-rules.reg-not-output] r[asm.naked-rules.noreturn] - Behavior is undefined if execution falls through to the end of the `naked_asm!` block. - - the assembly code is expected to contain a return instruction or to diverge + - 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 asm code, such as stack space allocated within the `naked_asm!` block. + - 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. @@ -1408,29 +1417,26 @@ 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. - -```txt +```rust # #[cfg(target_arch = "x86_64")] { -#[naked] +#[unsafe(naked)] extern "C-unwind" fn naked_function() { - unsafe { - 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, - ) - } + 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() { From 09c8e4a9dbba57c1dca948b160aac4ae400f8a72 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Mon, 21 Apr 2025 00:59:42 +0000 Subject: [PATCH 16/20] Remove normative cites to unsafe code guidelines doc The unsafe code guidelines document describes itself currently as largely abandoned and indicates that those points that have found consensus are documented in the Reference. Let's not then normatively cite that other document. Let's also remove a line about "these rules do not apply..." where "these" changed meaning when this line was copied from elsewhere. In the original, "these" meant rules about `readonly` and `nomem`, whereas here, it would have seemed to refer to the rules in the UCG, and that wouldn't make sense. --- src/inline-assembly.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 0c7049161..c682bb0dc 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -1264,7 +1264,6 @@ r[asm.rules.unwind] r[asm.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. - If the `readonly` option is set, then only memory reads are allowed. - If the `nomem` option is set then no reads or writes to memory are allowed. - These rules do not apply to memory which is private to the assembly code, such as stack space allocated within it. @@ -1405,8 +1404,6 @@ r[asm.naked-rules.noreturn] 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. From 27b569a6f20ba6dd57ce8adc0147ef77640072c8 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Mon, 21 Apr 2025 01:09:13 +0000 Subject: [PATCH 17/20] Sort some lists of things better --- src/attributes.md | 2 +- src/attributes/codegen.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/attributes.md b/src/attributes.md index cca010ca2..d6cbed613 100644 --- a/src/attributes.md +++ b/src/attributes.md @@ -44,8 +44,8 @@ The following attributes are unsafe: * [`export_name`] * [`link_section`] -* [`no_mangle`] * [`naked`] +* [`no_mangle`] r[attributes.kind] Attributes can be classified into the following kinds: diff --git a/src/attributes/codegen.md b/src/attributes/codegen.md index 5f77ed9e2..2a3340a2f 100644 --- a/src/attributes/codegen.md +++ b/src/attributes/codegen.md @@ -530,12 +530,12 @@ trait object whose methods are attributed. [`-C target-cpu`]: ../../rustc/codegen-options/index.html#target-cpu [`-C target-feature`]: ../../rustc/codegen-options/index.html#target-feature +[`inline`]: #the-inline-attribute [`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 +[`track_caller`]: #the-track-caller-attribute [`unused_variables`]: ../../rustc/lints/listing/warn-by-default.html#unused-variables [attribute]: ../attributes.md [attributes]: ../attributes.md From d64c438302db88d6056706670d953f1bd0e33684 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Mon, 21 Apr 2025 01:18:11 +0000 Subject: [PATCH 18/20] Make some editorial improvements --- src/attributes/codegen.md | 17 ++++++++++------- src/inline-assembly.md | 20 ++++++++------------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/attributes/codegen.md b/src/attributes/codegen.md index 2a3340a2f..025b7cc3e 100644 --- a/src/attributes/codegen.md +++ b/src/attributes/codegen.md @@ -56,28 +56,31 @@ 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. +No function prologue or epilogue is generated for the attributed function. The assembly code in the `naked_asm!` block constitutes 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. +The `naked` attribute is an [unsafe attribute]. Annotating a function with `#[unsafe(naked)]` comes with the safety obligation that the body must respect the function's calling convention, uphold its signature, and either return or diverge (i.e., not fall through past the end of the assembly code). 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. +The assembly code may assume that the call stack and register state are valid 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. +The assembly code may not be duplicated by the compiler except when monomorphizing polymorphic functions. + +> [!NOTE] +> Guaranteeing when the assembly code may or may not be duplicated is important for naked functions that define symbols. 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. +The [`inline`](#the-inline-attribute) attribute cannot by applied to a naked function. r[attributes.codegen.naked.track_caller] -A naked function cannot be attributed by the [`track_caller`](#the-track_caller-attribute) attribute. +The [`track_caller`](#the-track_caller-attribute) attribute cannot be applied to a naked function. r[attributes.codegen.naked.testing] -A naked function cannot be attributed by [the testing attributes](testing.md). +The [testing attributes](testing.md) cannot be applied to a naked function. r[attributes.codegen.no_builtins] ## The `no_builtins` attribute diff --git a/src/inline-assembly.md b/src/inline-assembly.md index c682bb0dc..981361ebe 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -81,8 +81,7 @@ 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). +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")] { @@ -1225,12 +1224,10 @@ unsafe { core::arch::asm!("mov {:e}, 1", out(reg) z, options(noreturn)); } ``` 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. +`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. +`global_asm!` only supports the `att_syntax` and `raw` options. The remaining options are not meaningful for global-scope inline assembly. ```rust,compile_fail # fn main() {} @@ -1391,16 +1388,15 @@ To avoid undefined behavior, these rules must be followed when using function-sc 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). + - 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. +- Any callee-saved registers must have the same value upon return as they had on entry. + - Caller-saved registers 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 +- Behavior is undefined if execution falls through past the end of the assembly code. + - 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. From ac16cabdeb6efe5ae8cbc7c0e6dcadfc07622244 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Mon, 21 Apr 2025 02:01:48 +0000 Subject: [PATCH 19/20] Annotate naked unwind example and add resources We demonstrate how to correctly unwind from a naked function. The assembler directives and boilerplate for this may be unfamiliar to people, so let's annotate this example heavily and add links to further resources. We'll use the `sysv64-unwind` ABI rather than `C-unwind` just to make things a bit more unambiguous. --- src/inline-assembly.md | 59 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 981361ebe..f11e9eece 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -1413,31 +1413,74 @@ r[asm.naked-rules.unwind] ```rust # #[cfg(target_arch = "x86_64")] { #[unsafe(naked)] -extern "C-unwind" fn naked_function() { +extern "sysv64-unwind" fn unwinding_naked() { core::arch::naked_asm!( + // "CFI" here stands for "call frame information". ".cfi_startproc", + // The CFA (canonical frame address) is the value of `rsp` + // before the `call`, i.e. before the return address, `rip`, + // was pushed to `rsp`, so it's eight bytes higher in memory + // than `rsp` upon function entry (after `rip` has been + // pushed). + // + // This is the default, so we don't have to write it. + //".cfi_def_cfa rsp, 8", + // + // The traditional thing to do is to preserve the base + // pointer, so we'll do that. "push rbp", - ".cfi_def_cfa_offset 16", + // Since we've now extended the stack downward by 8 bytes in + // memory, we need to adjust the offset to the CFA from `rsp` + // by another 8 bytes. + ".cfi_adjust_cfa_offset 8", + // We also then annotate where we've stored the caller's value + // of `rbp`, relative to the CFA, so that when unwinding into + // the caller we can find it, in case we need it to calculate + // the caller's CFA relative to it. + // + // Here, we've stored the caller's `rbp` starting 16 bytes + // below the CFA. I.e., starting from the CFA, there's first + // the `rip` (which starts 8 bytes below the CFA and continues + // up to it), then there's the caller's `rbp` that we just + // pushed. ".cfi_offset rbp, -16", + // As is traditional, we set the base pointer to the value of + // the stack pointer. This way, the base pointer stays the + // same throughout the function body. "mov rbp, rsp", + // We can now track the offset to the CFA from the base + // pointer. This means we don't need to make any further + // adjustments until the end, as we don't change `rbp`. ".cfi_def_cfa_register rbp", - "", - "call {function}", - "", + // We can now call a function that may panic. + "call {f}", + // Upon return, we restore `rbp` in preparation for returning + // ourselves. "pop rbp", + // Now that we've restored `rbp`, we must specify the offset + // to the CFA again in terms of `rsp`. ".cfi_def_cfa rsp, 8", + // Now we can return. "ret", ".cfi_endproc", - function = sym function_that_panics, + f = sym may_panic, ) } -extern "C-unwind" fn function_that_panics() { - panic!("unwind!"); +extern "sysv64-unwind" fn may_panic() { + panic!("unwind"); } # } ``` +> [!NOTE] +> +> For more information on the `cfi` assembler directives above, see these resources: +> +> - [Using `as` - CFI directives](https://sourceware.org/binutils/docs/as/CFI-directives.html) +> - [DWARF Debugging Information Format Version 5](https://dwarfstd.org/doc/DWARF5.pdf) +> - [ImperialViolet - CFI directives in assembly files](https://www.imperialviolet.org/2017/01/18/cfi.html) + r[asm.validity] ### Correctness and Validity From aca530a868c5114cdf6d444b8a8f1d1f796dc0cf Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Tue, 22 Apr 2025 18:44:34 +0000 Subject: [PATCH 20/20] Clarify rules on callee/caller saved registers For naked functions, let's break out the rules for callee and caller saved registers and name the rule identifiers more clearly. For the caller saved registers, let's drop the caveat about "even if they are not used for the return value", as it doesn't really add anything to the rule. --- src/inline-assembly.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index f11e9eece..fe97c1775 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -1390,9 +1390,11 @@ 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. - - Caller-saved registers may be used freely, even if they are not used for the return value. +r[asm.naked-rules.callee-saved-registers] +- All callee-saved registers must have the same value upon return as they had on entry. + +r[asm.naked-rules.caller-saved-registers] +- Caller-saved registers may be used freely. r[asm.naked-rules.noreturn] - Behavior is undefined if execution falls through past the end of the assembly code.