Add support for generating生成する
naked (prologue/epilogue-free) functions via a new function attribute.
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"
:::: "volatile");
let n = 0u64;
asm!("pop %rax"
:::: "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.
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;
}
}
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);
}
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.
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.みなす、考慮する
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
.
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.