Skip to content

Instantly share code, notes, and snippets.

@fwqaaq
Last active April 1, 2024 16:14
Show Gist options
  • Save fwqaaq/8f8701d194f5a87c958fd139a8624af7 to your computer and use it in GitHub Desktop.
Save fwqaaq/8f8701d194f5a87c958fd139a8624af7 to your computer and use it in GitHub Desktop.
Rust 利用 FFI 对接 C 语言

FFI

利用 build.rs 构建脚本,https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html#rustc-link-lib

主要是使用以下两个字段来构建:

其中 KIND 可以是以下值之一:

  • dylib:一个本地的动态库

  • static:一个本地的静态库

  • framework:一个 MacOS 框架

  • cargo:rustc-link-search=[KIND=]PATH — 告知 Cargo 通过 -L 将一个目录添加到依赖库的搜索路径中

其中 KIND 可以是以下值之一:

  • dependency: 表示要链接的库是一个 Cargo 依赖项。
  • crate: 表示要链接的库是一个 Rust crate。
  • native: 表示要链接的库是一个本地库,例如 C 或 C++ 库。
  • framework: 表示要链接的库是一个 MacOS 框架。
  • all: 表示要链接所有类型的库。这是默认值。

例如,我们使用 cargo new rust_c_exam 创建一个项目

  • 首先根目录下编写 add.c 文件
#include "add.h"

int add(int a, int b){
  return a + b;
}

并生成库文件

cc -c add.c -o add.o
ar rcs libadd.a add.o // 生成静态库文件
gcc -shared add.o -o libadd.so // 生成动态库文件

然后在 main.rs 中编写如下内容:

extern "C" {
    fn add(a: i32, b: i32) -> i32;
}

fn main() {
    println!("Hello, world!,{}", unsafe { add(4, 10) });
}

这时候使用 build.rs 构建脚本:

fn main() {
    println!("cargo:rustc-link-search=native=/Users/feiwu/Project/rust/rust_c_example");
    println!("cargo:rustc-link-lib=add"); // 静态链接库名称
}

link

这里,我们也可以使用 #[link] 配合 -L 参数来实现。

  • 修改 main.rs 中的代码
#[link(name = "add", kind = "dylib")]
extern "C" {
    fn add(a: i32, b: i32) -> i32;
}

fn main() {
    println!("Hello, world!,{}", unsafe { add(4, 10) });
}

使用 rsutc 编译,假设在根目录下:

# 指定链接库的位置
rustc src/main.rs -L .
# -L 的完整指定:
rustc src/main.rs -L native=.

生成指定类型的包

[lib] 下的 crate-type 属性,用于配置生成库的类型

  • bin:生成一个可执行文件(二进制文件)。
  • lib:生成由编译器首选的库类型,默认是 rlib
  • rlib:生成一个只包含 Rust 代码的静态库。
  • staticlib:生成一个本地的静态链接库。
  • dylib:生成一个 Rust 动态链接库。
  • cdylib:生成一个本地的动态链接库(类 C 得 dylib)。
  • proc-macro:生成一个过程宏库,用于编译时代码转换。

动态链接和静态链接的区别

一个可执行文件产生的过程:预处理(Preprocess)、编译(Compilation)、汇编(Assembly)以及链接(Linking)四个过程。

cc -E main.c -o main.i # 预编译
cc -S main.i -o main.s # 编译
as main.s -o main.o # 汇编 cc -c main.s -o main.o
ld # 链接

在 Linux 下,可以使用 ldd 查看可执行文件的链接库所在位置,而在 macOS 中则使用 otool -L 去查看。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment