我知道在Rust中调用C函数的方法之一是使用以下方式:
extern "C" {
fn abs(input: i32) -> i32;
}
fn main() {
unsafe {
println!("abs(-3) = {}", abs(-3));
}
}
但是如何在Rust代码中调用一些汇编函数(位于与其他Rust文件相同目录中的.s
文件内)呢?
这是在macOS上使用C x86_64调用约定的汇编函数。它不会崩溃,所以一定是正确的 :-)
Cargo.toml
[build-dependencies]
cc = "1.0"
build.rs
fn main() {
cc::Build::new()
.file("add.S")
.compile("my-asm-lib");
}
add.S
.text
.globl _my_adder
_my_adder:
addq %rdi, %rsi
movq %rsi, %rax
ret
src/main.rs
extern "C" {
fn my_adder(a: u64, b: u64) -> u64;
}
fn main() {
let sum = unsafe { my_adder(1, 2) };
println!("{}", sum);
}
要使用汇编函数,有两个主要步骤:
Rust 并没有内置编译汇编文件的方式。相反,你需要使用机器上已经存在的 C 编译器进行编译。最常用的方法是在 构建脚本 中使用 cc crate。
构建脚本将会把汇编文件编译成静态库,并且告诉 Cargo 去链接这个库。
你必须确保你的汇编语法对使用的 C 编译器是有效的。例如,MSVC 和 GCC 使用不同的汇编风格。你可能需要有多份相同的汇编函数来处理不同的平台。
调用函数高度依赖于调用约定。主要的调用约定是C 调用约定,通过 extern "C"
或者只使用 extern
表示。你可能还需要使用 #[link_name = "..." ]
或者 #[no_mangle]
属性。
你还可以使用 #[naked]
函数创建自定义调用约定(参见 RFC 1201)。然后,根据其调用约定,在调用方代码中在调用前/后使用汇编代码。这在稳定的 Rust 中不可用。
Rust 1.21.1 还支持许多其他的调用约定:
cdecl、stdcall、fastcall、vectorcall、thiscall、aapcs、win64、sysv64、ptx-kernel、msp430-interrupt、x86-interrupt、Rust、C、system、rust-intrinsic、rust-call、platform-intrinsic、unadjusted
gcc
而不是$TARGET-gcc
。我该如何更改呢? - piegames如果您使用夜间版的Rust,则可以通过使用global_asm!宏避免使用自定义构建脚本。
A simple usage looks like this:
#[feature(global_asm)] ... global_asm!(include_str!("something_neato.s"));