文盘Rust -- FFI 浅尝 | 京东云技术团队

rust FFI 是rust与其他语言互调的桥梁,通过FFI rust 可以有效继承 C 语言的历史资产。本期通过几个例子来聊聊rust与C 语言交互的具体步骤。

场景一 调用C代码

创建工程

1cargo new --bin ffi_sample 2 3 4 5

Cargo.toml 配置

1[package] 2name = "ffi_sample" 3version = "0.1.0" 4edition = "2021" 5build = "build.rs" 6 7# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 9[build-dependencies] 10cc = "1.0.79" 11 12[dependencies] 13libc = "0.2.146" 14libloading = "0.8.0" 15 16 17 18

编写一个简单的c程序sample.c

1int add(int a,int b){ 2 return a+b; 3} 4 5 6 7

main.rs

1use std::os::raw::c_int; 2 3 4#[link(name = "sample")] 5extern "C" { 6 fn add(a: c_int, b: c_int) -> c_int; 7} 8 9fn main() { 10 let r = unsafe { add(2, 18) }; 11 println!("{:?}", r); 12} 13 14 15 16

build.rs

1 2fn main() { 3 cc::Build::new().file("sample.c").compile("sample"); 4} 5 6 7 8 9

代码目录树

1. 2├── Cargo.lock 3├── Cargo.toml 4├── build.rs 5├── sample.c 6└── src 7    └── main.rs 8 9 10 11
1cargo run 2 3 4 5

场景二 使用bindgen 通过头文件绑定c语言动态链接库

修改Cargo.toml,新增bindgen依赖

1[package] 2name = "ffi_sample" 3version = "0.1.0" 4edition = "2021" 5build = "build.rs" 6 7# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 9[build-dependencies] 10cc = "1.0.79" 11bindgen = "0.65.1" 12 13[dependencies] 14libc = "0.2.146" 15libloading = "0.8.0" 16 17 18 19

新增 sample.h 头文件

1#ifndef ADD_H 2#define ADD_H 3 4int add(int a, int b); 5 6#endif 7 8 9 10

新增 wrapper.h 头文件 wrapper.h 文件将包括所有各种头文件,这些头文件包含我们想要绑定的结构和函数的声明

1#include "sample.h"; 2 3 4 5

改写build.rs 编译 sample.c 生成动态链接库sample.so;通过bindgen生成rust binding c 的代码并输出到 bindings 目录

1use std::path::PathBuf; 2 3fn main() { 4 // 参考cc 文档 5 println!("cargo:rerun-if-changed=sample.c"); 6 cc::Build::new() 7 .file("sample.c") 8 .shared_flag(true) 9 .compile("sample.so"); 10 // 参考 https://doc.rust-lang.org/cargo/reference/build-scripts.html 11 println!("cargo:rustc-link-lib=sample.so"); 12 println!("cargo:rerun-if-changed=sample.h"); 13 let bindings = bindgen::Builder::default() 14 .header("wrapper.h") 15 .parse_callbacks(Box::new(bindgen::CargoCallbacks)) 16 .generate() 17 .expect("Unable to generate bindings"); 18 19 let out_path = PathBuf::from("bindings"); 20 bindings 21 .write_to_file(out_path.join("sample_bindings.rs")) 22 .expect("Couldn't write bindings!"); 23} 24 25 26 27

修改main.rs include 宏引入sample 动态链接库的binding。以前我们自己手写的C函数绑定就不需要了,看看bindings/sample_bindings.rs 的内容与我们手写的函数绑定是等效的

1include!("../bindings/sample_bindings.rs"); 2 3// #[link(name = "sample")] 4// extern "C" { 5// fn add(a: c_int, b: c_int) -> c_int; 6// } 7 8fn main() { 9 let r = unsafe { add(2, 18) }; 10 println!("{:?}", r); 11} 12 13 14 15 16

代码目录树

1. 2├── Cargo.lock 3├── Cargo.toml 4├── bindings 5│   └── sample_bindings.rs 6├── build.rs 7├── sample.c 8├── sample.h 9├── src 10│   └── main.rs 11└── wrapper.h 12 13 14 15

ffi_sample 工程的完整代码位置,读者可以clone https://github.com/jiashiwen/wenpanrust,直接运行即可

1cargo run -p ffi_sample 2 3 4 5

场景三 封装一个c编写的库

secp256k1是一个椭圆曲线计算的 clib,这玩意儿在密码学和隐私计算方面的常用算法,下面我们从工程方面看看封装secp256k1如何操作

1cargo new --lib wrapper_secp256k1 2 3 4 5

cargo.toml

1[package] 2name = "wrapper_secp256k1" 3version = "0.1.0" 4edition = "2021" 5 6# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7[build-dependencies] 8cc = "1.0.79" 9bindgen = "0.65.1" 10 11[dependencies] 12 13 14 15

git 引入 submodule

1cd wrapper_secp256k1 2git submodule add https://github.com/bitcoin-core/secp256k1 wrapper_secp256k1/secp256k1_sys 3 4 5 6

工程下新建bindings目录用来存放绑定文件,该目录与src平级

wrapper.h

1#include "secp256k1_sys/secp256k1/include/secp256k1.h" 2 3 4 5

build.rs

1use std::path::PathBuf; 2 3fn main() { 4 println!("cargo:rustc-link-lib=secp256k1"); 5 println!("cargo:rerun-if-changed=wrapper.h"); 6 let bindings = bindgen::Builder::default() 7 .header("wrapper.h") 8 .parse_callbacks(Box::new(bindgen::CargoCallbacks)) 9 .generate() 10 .expect("Unable to generate bindings"); 11 12 let out_path = PathBuf::from("bindings"); 13 bindings 14 .write_to_file(out_path.join("bindings.rs")) 15 .expect("Couldn't write bindings!"); 16} 17 18 19 20

cargo build 通过

编写测试 lib.rs

1include!("../bindings/secp256k1.rs"); 2 3#[cfg(test)] 4mod tests { 5 use super::*; 6 7 #[test] 8 fn test_create_pubkey() { 9 // secp256k1返回公钥 10 let mut pubkey: secp256k1_pubkey = secp256k1_pubkey { data: [0; 64] }; 11 let prikey: u8 = 1; 12 13 unsafe { 14 let context = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); 15 assert!(!context.is_null()); 16 let ret = secp256k1_ec_pubkey_create(&*context, &mut pubkey, &prikey); 17 assert_eq!(ret, 1); 18 } 19 } 20} 21 22 23 24

运行测试 cargo test 报错

1warning: `wrapper_secp256k1` (lib) generated 5 warnings 2error: linking with `cc` failed: exit status: 1 3 | 4 = note: LC_ALL="C" PATH="/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/bin:/Users/jiashiwen/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libobject-6d1da0e5d7930106.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libmemchr-d6d74858e37ed726.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libaddr2line-d75e66c6c1b76fdd.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libgimli-546ea342344e3761.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/librustc_demangle-8ad10e36ca13f067.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libstd_detect-0543b8486ac00cf6.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libhashbrown-7f0d42017ce08763.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libminiz_oxide-65e6b9c4725e3b7f.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libadler-131157f72607aea7.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/librustc_std_workspace_alloc-f7d15060b16c135d.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libunwind-a52bfac5ae872be2.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libcfg_if-1762d9ac100ea3e7.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/liblibc-f8e0e4708f61f3f4.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/liballoc-af9a608dd9cb26b2.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/librustc_std_workspace_core-9777023438fd3d6a.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libcore-83ca6d61eb70e9b8.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libcompiler_builtins-ea2ca6e1df0449b8.rlib" "-lSystem" "-lc" "-lm" "-L" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib" "-o" "/Users/jiashiwen/rustproject/wrapper_secp256k1/target/debug/deps/wrapper_secp256k1-4bf30c62ecfdf2a7" "-Wl,-dead_strip" "-nodefaultlibs" 5 = note: ld: library not found for -lsecp256k1 6 clang: error: linker command failed with exit code 1 (use -v to see invocation) 7 8 9warning: `wrapper_secp256k1` (lib test) generated 5 warnings (5 duplicates) 10error: could not compile `wrapper_secp256k1` (lib test) due to previous error; 5 warnings emitted 11 12 13 14

报错显示找不到编译 secp256k1 相对应的库。

手动编译secp256k1

1cd secp256k1_sys 2./autogen.sh 3./configure 4make 5make install 6 7 8 9

编译完成后,测试通过

其实 secp256k1 有对应的 rust wrapper,我们这里只是展示一下封装的过程。

wrapper_secp256k1 工程的完整代码位置,有兴趣的朋友可以clone https://github.com/jiashiwen/wenpanrust。通过以下操作查看运行结果:

  • clone 项目

    1git clone https://github.com/jiashiwen/wenpanrust 2cd wenpanrust 3 4 5 6
  • update submodule

    1git submodule init 2git submodule update 3 4 5 6
  • 编译 secp256k1

    1cd wrapper_secp256k1/secp256k1_sys 2./autogen.sh 3./configure 4make 5make install 6 7 8 9
  • run test

    1cargo test -p wrapper_secp256k1 2 3 4 5

参考资料

Rust FFI (C vs Rust)学习杂记.pdf
bindgen官方文档
Rust FFI 编程 - bindgen 使用示例

作者:京东科技 贾世闻

来源:京东云开发者社区

点赞
收藏

评论区

加载中...

相关推荐

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

Rust学习笔记#6:所有权系统

!(https://oscimg.oschina.net/oscnet/up0b8d4b9e5e3854503a73fd494cd4b53d984.JPEG)引子:段错误与内存安全在刚开始接触Rust的时候,我们就提过Rust语言的定位:Rustisasystem'sprogramminglanguagethatr

FLV文件格式

1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  

Rust开发环境搭建

1.Rust概述按照百度百科的说法,Rust是一门系统编程语言,专注于安全,尤其是并发安全,支持函数式和命令式以及泛型等编程范式的多范式语言。Rust在语法上和C类似,但是设计者想要在保证性能的同时提供更好的内存安全。Rust最初是由Mozilla研究院的GraydonHoare设计创造,然后在DaveHerman,Brend

mysql设置时区

mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0