Rust FFI 入门:extern、ABI 与底层符号链接解析
Table of Contents
Rust FFI 入门:extern、ABI 与底层符号链接解析
Rust 以内存安全著称,但有时我们需要打破边界,与 C/C++ 等外部代码交互,或者直接进行底层操作,这就是 外部函数接口(FFI) 的用武之地。作为一篇入门指南,我们将通过一个精简的 Rust 代码示例,聚焦于 FFI 的三大基石:extern
块、调用约定(ABI) 和符号链接。学习如何使用它们,是您迈向 Rust 高级系统编程的关键第一步。
Rust FFI 是连接底层世界的桥梁。本文从入门角度出发,详细解析 extern
块、ABI 调用约定、#[no_mangle]
符号导出 和 #[link_name]
别名这四大核心概念,通过实战代码,帮助你掌握 Rust FFI 的底层符号链接机制。
实操:Rust 内部的符号链接机制
Rust FFI 核心代码
// Rust is highly capable of sharing FFI interfaces with C/C++ and other statically compiled
// languages, and it can even link within the code itself! It makes it through the extern
// block, just like the code below.
//
// The short string after the `extern` keyword indicates which ABI the externally imported
// function would follow. In this exercise, "Rust" is used, while other variants exists like
// "C" for standard C ABI, "stdcall" for the Windows ABI.
//
// The externally imported functions are declared in the extern blocks, with a semicolon to
// mark the end of signature instead of curly braces. Some attributes can be applied to those
// function declarations to modify the linking behavior, such as #[link_name = ".."] to
// modify the actual symbol names.
//
// If you want to export your symbol to the linking environment, the `extern` keyword can
// also be marked before a function definition with the same ABI string note. The default ABI
// for Rust functions is literally "Rust", so if you want to link against pure Rust functions,
// the whole extern term can be omitted.
//
// Rust mangles symbols by default, just like C++ does. To suppress this behavior and make
// those functions addressable by name, the attribute #[no_mangle] can be applied.
extern "Rust" {
fn my_demo_function(a: u32) -> u32;
#[link_name = "my_demo_function"]
fn my_demo_function_alias(a: u32) -> u32;
}
mod Foo {
// No `extern` equals `extern "Rust"`.
#[no_mangle]
fn my_demo_function(a: u32) -> u32 {
a
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_success() {
// The externally imported functions are UNSAFE by default
// because of untrusted source of other languages. You may
// wrap them in safe Rust APIs to ease the burden of callers.
//
// SAFETY: We know those functions are aliases of a safe
// Rust function.
unsafe {
my_demo_function(123);
my_demo_function_alias(456);
}
}
}
这段 Rust 代码详细展示了 外部函数接口(FFI) 在 Rust 内部的运用,主要围绕如何使用 extern
块进行函数声明和符号链接。
核心机制和作用:
这段代码通过使用 extern "Rust" { ... }
块来**导入(或声明)**函数签名,尽管这些函数的实际定义 (my_demo_function
) 存在于代码的另一个模块 Foo
内部。这种模式通常用于与其他语言(如 C/C++)或动态库进行交互,但在本例中,它被用于演示 Rust 内部的符号链接机制。
extern
块与 ABI:extern "Rust" { ... }
定义了一个外部块,其中"Rust"
指定了调用约定(ABI)。这意味着被声明的函数将遵循 Rust 编译器默认的调用规则。其他常见的 ABI 包括"C"
(标准 C 语言 ABI)或"stdcall"
(Windows ABI)。- 符号导出与
#[no_mangle]
: 在Foo
模块内部定义的函数my_demo_function
前使用了#[no_mangle]
属性。通常,Rust 会像 C++ 一样对函数名进行符号重整(Name Mangling),使其在二进制文件中难以直接通过名称查找。#[no_mangle]
阻止了这一行为,确保了函数在链接环境中有一个干净、可预测的名称 (my_demo_function
)。 - 符号别名与
#[link_name]
: 在extern
块中,my_demo_function_alias
明确使用了#[link_name = "my_demo_function"]
属性。这告诉链接器,尽管函数在 Rust 代码中被命名为my_demo_function_alias
,但它应该链接到外部环境中名为my_demo_function
的实际符号上。 - 不安全调用: 任何在
extern
块中声明的函数,无论是链接到 C 代码还是像本例中链接到 Rust 代码,默认都被视为不安全(unsafe
)。这是因为编译器无法验证外部函数是否满足 Rust 的内存安全和并发安全规则。因此,在测试用例中,对my_demo_function
及其别名的调用必须被包裹在unsafe { ... }
块内,以此作为程序员对内存安全的承诺。
总之,这段代码是理解 Rust FFI 和链接机制的基础示例,展示了如何使用 extern
、#[no_mangle]
和 #[link_name]
来控制函数符号的导入和导出,从而实现跨语言或跨模块的低级别通信。
总结
这段代码是理解 Rust FFI 和符号链接机制的绝佳示例。它展示了三个核心要素:通过 extern
确定调用约定(ABI),通过 #[no_mangle]
导出清晰的函数符号,以及通过 #[link_name]
创建符号别名。
掌握这些属性,意味着您获得了在 Rust 安全框架下,与底层内存和外部代码进行高性能、低级别通信的能力。但请务必记住,任何 FFI 调用都是对内存安全的主动接管,必须严格履行安全契约。
参考
- https://doc.rust-lang.org/nomicon/ffi.html
- https://www.reddit.com/r/rust/comments/18vnsf2/rusts_ffi_with_c/
- https://doc.rust-lang.org/rust-by-example/std_misc/ffi.html
- https://nomicon.purewhite.io/ffi.html
- https://doc.rust-lang.org/cargo/reference/build-scripts.html
- https://make.mad-scientist.net/papers/jobserver-implementation/
- https://github.com/rust-lang/rust-bindgen
- https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/book/first-edition/ffi.html
- https://github.com/wisonye/rust-ffi-demo
- https://www.reddit.com/r/rust/comments/umr7oo/how_does_rust_python_ffi_work/
- https://github.com/PyO3/pyo3
- https://dora-rs.ai/blog/rust-python/
- https://users.rust-lang.org/t/calling-into-rust-from-python/8923