Summary

Add support for generating

生成する
naked (prologue/epilogue-free) functions via a new function attribute.

Motivation

Some systems programming tasks require that the programmer have complete

完全な
control
制御する
over function stack layout and interpretation,
解釈
generally in cases where the compiler lacks support for a specific
特定の
use case. While these cases can be addressed by building the requisite code with external tools and linking with Rust, it is advantageous to allow
許可する、可能にする
the Rust compiler to drive the entire process, particularly in that code may be generated
生成する
via monomorphization or macro expansion.

When writing interrupt handlers for example, most systems require additional

追加の
state be saved beyond the usual ABI requirements. To avoid
避ける、回避する
corrupting program state, the interrupt handler must save the registers which might be modified before handing control
制御する
to compiler-generated code. Consider
考える、みなす
a contrived interrupt handler for x86_64:

#![allow(unused)] fn main() { unsafe fn isr_nop() { asm!("push %rax" /* Additional pushes elided */ :::: "volatile"); let n = 0u64; asm!("pop %rax" /* Additional pops elided */ :::: "volatile"); } }

The generated

生成する
assembly for this function might resemble the following (simplified for readability):

isr_nop: sub $8, %rsp push %rax movq $0, 0(%rsp) pop %rax add $8, %rsp retq

Here the programmer's need to save machine state conflicts with the compiler's assumption that it has complete

完全な
control
制御する
over stack layout, with the result
結果、戻り値
that the saved value of rax is clobbered by the compiler. Given
与えられた
that details of stack layout for any given
与えられた
function are not predictable (and may change with compiler version or optimization settings), attempting to predict the stack layout to sidestep this issue is infeasible.

When interacting with FFIs that are not natively supported by the compiler, a similar

似ている、同様の
situation arises where the programmer knows the expected calling
呼び出し
convention and can implement
実装する
a translation between the foreign ABI and one supported by the compiler.

Support for naked functions also allows

許可する、可能にする
programmers to write functions that would otherwise
さもなければ
be unsafe, such as the following snippet
断片
which returns the address of its caller
呼び出し側
when called
呼び出し
with the C ABI on x86.

mov 4(%ebp), %eax ret

Because the compiler depends on a function prologue and epilogue to maintain storage

保存場所、保管スペース
for local variable
変数、ストレージ
bindings, it is generally unsafe to write anything but inline assembly inside a naked function. The LLVM language
言語
reference
参照
describes
記述する
this feature as having "very system-specific consequences",
結果
which the programmer must be aware of.

Detailed design
設計(する)

Add a new function attribute to the language,

言語
#[naked], indicating the function should have prologue/epilogue emission disabled.

Because the calling

呼び出し
convention of a naked function is not guaranteed
保証する
to match
一致する、マッチさせる
any calling
呼び出し
convention the compiler is compatible with, calls
呼び出し
to naked functions from within Rust code are forbidden unless the function is also declared
宣言
with a well-defined ABI.

Defining

定義する
a naked function with the default (Rust) ABI is an error, because the Rust ABI is unspecified
特定されていない
and the programmer can never write a function which is guaranteed
保証する
to be compatible. For example, The function declaration
宣言
of foo in the following code block is an error.

#![allow(unused)] fn main() { #[naked] unsafe fn foo() { } }

The following variant is not an error because the C calling

呼び出し
convention is well-defined and it is thus
それゆえに、従って、
possible for the programmer to write a conforming function:

#![allow(unused)] fn main() { #[naked] extern "C" fn foo() { } }

Because the compiler cannot verify the correctness of code written in a naked function (since it may have an unknown calling

呼び出し
convention), naked functions must be declared
宣言
unsafe or contain
含む
no non-unsafe statements
in the body. The function error in the following code block is a compile-time error, whereas the functions correct1 and correct2 are permitted.
許す

#[naked] extern "C" fn error(x: &mut u8) { *x += 1; } #[naked] unsafe extern "C" fn correct1(x: &mut u8) { *x += 1; } #[naked] extern "C" fn correct2(x: &mut u8) { unsafe { *x += 1; } }

Example

The following example illustrates

描写する、示す
the possible use of a naked function for implementation
実装
of an interrupt service routine on 32-bit x86.

use std::intrinsics; use std::sync::atomic::{self, AtomicUsize, Ordering}; #[naked] #[cfg(target_arch="x86")] unsafe extern "C" fn isr_3() { asm!("pushad call increment_breakpoint_count popad iretd" :::: "volatile"); intrinsics::unreachable(); } static bp_count: AtomicUsize = ATOMIC_USIZE_INIT; #[no_mangle] pub fn increment_breakpoint_count() { bp_count.fetch_add(1, Ordering::Relaxed); } fn register_isr(vector: u8, handler: unsafe extern "C" fn() -> ()) { /* ... */ } fn main() { register_isr(3, isr_3); // ... }

Implementation
実装
Considerations
考慮

The current support for extern functions in rustc generates a minimum

最小の
of two basic blocks for any function declared
宣言
in Rust code with a non-default calling
呼び出し
convention: a trampoline which translates the declared
宣言
calling
呼び出し
convention to the Rust convention, and a Rust ABI version of the function containing
含む
the actual
実際の
implementation.
実装
Calls
呼び出し
to the function from Rust code call
呼び出し
the Rust ABI version directly.
直接

For naked functions, it is impossible for the compiler to generate a Rust ABI version of the function because the implementation

実装
may depend on the calling
呼び出し
convention. In cases where calling
呼び出し
a naked function from Rust is permitted,
許す
the compiler must be able to use the target calling
呼び出し
convention directly
直接
rather than call
呼び出し
the same function with the Rust convention.

Drawbacks

The utility of this feature is extremely limited to most users, and it might be misused if the implications of writing a naked function are not carefully considered.

みなす、考慮する

Alternatives
代わりのもの、選択肢

Do nothing. The required functionality for the use case outlined can be implemented

実装する
outside Rust code and linked in as needed. Support for additional
追加の
calling
呼び出し
conventions could be added
たす
to the compiler as needed, or emulated with external libraries such as libffi.

Unresolved questions

It is easy to quietly generate wrong code in naked functions, such as by causing the compiler to allocate stack space for temporaries

一時的な
where none were anticipated. There is currently no restriction
制限
on writing Rust statements
inside a naked function, while most compilers supporting similar
似ている、同様の
features either require or strongly recommend that authors write only inline assembly inside naked functions to ensure
保証する
no code is generated
生成する
that assumes a particular stack layout. It may be desirable to place further
さらなる、それ以上
restrictions
制限
on what statements
are permitted
許す
in the body of a naked function, such as permitting
許す
only asm! statements.

The unsafe requirement on naked functions may not be desirable in all cases. However, relaxing that requirement in the future would not be a breaking change.

Because a naked function may use a calling

呼び出し
convention unknown to the compiler, it may be useful to add a "unknown" calling
呼び出し
convention to the compiler which is illegal
文法違反
to call
呼び出し
directly.
直接
Absent
無い、存在しない
this feature, functions implementing
実装する
an unknown ABI would need to be declared
宣言
with a calling
呼び出し
convention which is known to be incorrect and depend on the programmer to avoid
避ける、回避する
calling
呼び出し
such a function incorrectly since it cannot be prevented
防ぐ
statically.