# 文盘Rust -- FFI 浅尝

news2024/12/25 14:02:30

作者: jiashiwen 原文来源: https://tidb.net/blog/cfa03c39

notice"Rust is a trademark of the Mozilla Foundation in the US and other countries."

no-alt

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

场景一 调用C代码

创建工程

cargo new --bin ffi_sample

Cargo.toml 配置

[package]
name = "ffi_sample"
version = "0.1.0"
edition = "2021"
build = "build.rs"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[build-dependencies]
cc = "1.0.79"

[dependencies]
libc = "0.2.146"
libloading = "0.8.0"

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

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

main.rs

use std::os::raw::c_int;


#[link(name = "sample")]
extern "C" {
    fn add(a: c_int, b: c_int) -> c_int;
}

fn main() {
    let r = unsafe { add(2, 18) };
    println!("{:?}", r);
}

build.rs


fn main() {
    cc::Build::new().file("sample.c").compile("sample");
}

代码目录树

.
├── Cargo.lock
├── Cargo.toml
├── build.rs
├── sample.c
└── src
    └── main.rs
cargo run

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

修改Cargo.toml,新增bindgen依赖

[package]
name = "ffi_sample"
version = "0.1.0"
edition = "2021"
build = "build.rs"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[build-dependencies]
cc = "1.0.79"
bindgen = "0.65.1"

[dependencies]
libc = "0.2.146"
libloading = "0.8.0"

新增 sample.h 头文件

#ifndef ADD_H
#define ADD_H

int add(int a, int b);

#endif

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

#include "sample.h";

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

use std::path::PathBuf;

fn main() {
    // 参考cc 文档
    println!("cargo:rerun-if-changed=sample.c");
    cc::Build::new()
        .file("sample.c")
        .shared_flag(true)
        .compile("sample.so");
    // 参考 https://doc.rust-lang.org/cargo/reference/build-scripts.html
    println!("cargo:rustc-link-lib=sample.so");
    println!("cargo:rerun-if-changed=sample.h");
    let bindings = bindgen::Builder::default()
        .header("wrapper.h")
        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
        .generate()
        .expect("Unable to generate bindings");

    let out_path = PathBuf::from("bindings");
    bindings
        .write_to_file(out_path.join("sample_bindings.rs"))
        .expect("Couldn't write bindings!");
}

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

include!("../bindings/sample_bindings.rs");

// #[link(name = "sample")]
// extern "C" {
//     fn add(a: c_int, b: c_int) -> c_int;
// }

fn main() {
    let r = unsafe { add(2, 18) };
    println!("{:?}", r);
}

代码目录树

.
├── Cargo.lock
├── Cargo.toml
├── bindings
│   └── sample_bindings.rs
├── build.rs
├── sample.c
├── sample.h
├── src
│   └── main.rs
└── wrapper.h

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

cargo run -p ffi_sample

即可

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

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

cargo new --lib wrapper_secp256k1

cargo.toml

[package]
name = "wrapper_secp256k1"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
cc = "1.0.79"
bindgen = "0.65.1"

[dependencies]

git 引入 submodule

cd wrapper_secp256k1
git submodule add https://github.com/bitcoin-core/secp256k1  wrapper_secp256k1/secp256k1_sys 

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

wrapper.h

#include "secp256k1_sys/secp256k1/include/secp256k1.h"

build.rs

use std::path::PathBuf;

fn main() {
    println!("cargo:rustc-link-lib=secp256k1");
    println!("cargo:rerun-if-changed=wrapper.h");
    let bindings = bindgen::Builder::default()
        .header("wrapper.h")
        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
        .generate()
        .expect("Unable to generate bindings");

    let out_path = PathBuf::from("bindings");
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");
}

cargo build 通过

编写测试 lib.rs

include!("../bindings/secp256k1.rs");

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_create_pubkey() {
        // secp256k1返回公钥
        let mut pubkey: secp256k1_pubkey = secp256k1_pubkey { data: [0; 64] };
        let prikey: u8 = 1;

        unsafe {
            let context = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
            assert!(!context.is_null());
            let ret = secp256k1_ec_pubkey_create(&*context, &mut pubkey, &prikey);
            assert_eq!(ret, 1);
        }
    }
}

运行测试 cargo test 报错

warning: `wrapper_secp256k1` (lib) generated 5 warnings
error: linking with `cc` failed: exit status: 1
  |
  = 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"
  = note: ld: library not found for -lsecp256k1
          clang: error: linker command failed with exit code 1 (use -v to see invocation)


warning: `wrapper_secp256k1` (lib test) generated 5 warnings (5 duplicates)
error: could not compile `wrapper_secp256k1` (lib test) due to previous error; 5 warnings emitted

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

手动编译secp256k1

cd secp256k1_sys
./autogen.sh
./configure
make
make install

编译完成后,测试通过

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

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

  • clone 项目

    git clone https://github.com/jiashiwen/wenpanrust
    cd wenpanrust
    
  • update submodule

    git submodule init
    git submodule update
    
  • 编译 secp256k1

    cd wrapper_secp256k1/secp256k1_sys 
    ./autogen.sh
    ./configure
    make
    make install  
    
  • run test

    cargo test -p wrapper_secp256k1
    

参考资料

Rust FFI (C vs Rust)学习杂记.pdf

bindgen官方文档

Rust FFI 编程 - bindgen 使用示例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/734141.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

道德与社会问题简报 #4: 文生图模型中的偏见

简而言之: 我们需要更好的方法来评估文生图模型中的偏见 介绍 文本到图像 (TTI) 生成 现在非常流行,成千上万的 TTI 模型被上传到 Hugging Face Hub。每种模态都可能受到不同来源的偏见影响,这就引出了一个问题: 我们如何发现这些模型中的偏见&#xff1…

Android启动流程优化 上篇

Android启动流程优化 上篇 本文链接:Android启动流程优化 上篇_猎羽的博客-CSDN博客 启动流程 各个阶段图 1、各个阶段的概括总结 分为5个大阶段或者10个小阶段 【字节跳动团队】内部论坛分享也是这么处理的 补充一些只是细节点: application#onCreate()运行…

基于多案例系统学习防洪评价报告编制方法与水流数学模型建模(HECRAS、MIKE、EFDC、Delft3D、FVCOM、SWAT、SWMM等模型应用)

目录 ​专题一 《防洪评价报告编制导则解读河道管理范围内建设项目编制导则》(SL/T808- 2021)解读编制导则解读 专题二 防洪评价相关制度与解析 ★专题三 案例演练解析 专题四 防洪评价地形获取及常用计算实践 专题五 HEC-RAS软件原理及应用案例解析…

leetcode160.相交链表

https://leetcode.cn/problems/intersection-of-two-linked-lists/solution/ 相交链表是指两个单向链表在某个节点处相交,之后形成了共同的后续部分。通常,两个链表的长度不相等。在相交节点之前,两个链表的节点数可能不同,但在相…

Java后端编译与优化

如果我们将字节码看作是程序语言的一种中间表示形式,那编译器无论在何时、在何种状态下把Class文件转换成与本地基础设施相关的二进制机器码,它都可以视为整个编译过程的后端。 1 即时编译器 即时编译器是指运行时将热点代码编译成本地机器码&#xff…

程序员必须掌握哪些算法?——前端开发工程师需要掌握的算法

文章目录 📋前言🎯什么是算法?🎯前端开发工程师需要掌握的算法🧩排序算法(如快速排序、归并排序)🧩搜索算法(如二分搜索)🧩图算法(如广…

【Unity造轮子】2D横版平台跳跃游戏实现多段跳完美手感(含源码)

文章目录 前言先看效果,手感很丝滑原理开始1. 看到检测点的检测范围2. 二段跳实现3. 动画控制器配置 源码扩展完结 前言 随着游戏技术的不断发展,2D横版平台跳跃游戏成为许多玩家的最爱。这类游戏以其简单而有趣的玩法和精致的视觉效果吸引着无数游戏爱…

Kotlin~Decorator装饰器模式

概念 装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。可以运行时动态添加新的功能,而无需改变原来的代码。 特点: 灵活扩展新的功能动态…

接口设计的总结

目录 前言 设计好接口的 36 个锦囊 总结 前言 作为后端开发,不管是什么语言,Java、Go 还是 C,其背后的后端思想都是类似的。我们做后端开发工程师,主要工作就是:如何把一个接口设计好。所以,今天就给大家…

北京大学考研机试题:二叉树

【题目来源】https://www.acwing.com/problem/content/description/3474/【题目描述】 如上图所示,由正整数 1,2,3…… 组成了一颗特殊二叉树。 我们已知这个二叉树的最后一个结点是 n。 现在的问题是,结点 m 所在的子树中一共包括…

MobileNeRF在Windows上的配置

MobileNeRF于2023年提出,源码地址:https://github.com/google-research/jax3d/tree/main/jax3d/projects/mobilenerf ,论文为:《MobileNeRF: Exploiting the Polygon Rasterization Pipeline for Efficient Neural Field Renderin…

QT自定义工具条渐变背景颜色一例

使用样式定义: QWidget* toolbar new QWidget(this);toolbar->setObjectName("main_tool");toolbar->setStyleSheet("#main_tool{background: qlineargradient(x1:0 , y1:0 , x2:1 , y2:0,""stop:0 rgba(0,255,0, 0.2),"&q…

Nuxt3 components公共组件

1.目录结构 效果实现:写一个公共的right组件,在index和about页面同时引用它 2.index页面引入 index页面显示效果 about页面引用及效果

组件的自定义事件

src/components/School.vue <template><div class"school"><h2>学校名称&#xff1a;{{ name}}</h2><h2>学校地址&#xff1a;{{ address }}</h2><button click"sendSchoolName">把学校名给App</button>…

数据集成的强大联盟:Elasticsearch、Kibana、Logstash、MySQL

通常&#xff0c;很多关系数据项目都使用 MySQL。 它对于标准的 CRUD 操作是有益的&#xff0c;但有时我们需要做额外的过程。 当我们搜索某些内容时&#xff0c;我们会消耗资源或合并多个表。 有时&#xff0c;即使不是&#xff0c;可能仍然需要复杂的 SQL 查询。 也许这不是正…

如何查看OpenAI的AccessToken?

如何查看OpenAI的AccessToken&#xff1f; 记录一下如何查看 OpenAI的 AccessToken 文章目录 如何查看OpenAI的AccessToken&#xff1f;前提具体操作总结 前提 &#x1f4a7;首先&#xff0c;在获取AccessToken前&#xff0c;你需要达成 以下两个条件: 拥有一个可用的OpenA…

ARM CORETEX M0简介

ARM CORETEX M0简介 1. M0处理器简单框图 处理器内核&#xff1a;逻辑控制与运算 内部总线系统&#xff1a;单总线将处理器与外部的存储器和外部连接&#xff0c;进行数据交互&#xff08;冯诺依曼架构&#xff0c;数据与指令一起&#xff09; NVIC&#xff1a;嵌套向量中断控…

【Matlab】智能优化算法_亨利气体溶解度优化算法HGSO

【Matlab】智能优化算法_亨利气体溶解度优化算法HGSO 1.背景介绍2.数学模型2.1 亨利定律2.2 HGSO 3.文件结构4.伪代码5.详细代码及注释5.1 Create_Groups.m5.2 Evaluate.m5.3 fun_checkpoisions.m5.4 fun_getDefaultOptions.m5.5 HGSO.m5.6 main.m5.7 sumsqu.m5.8 update_posit…

机器学习总览

机器学习 1.什么是机器学习&#xff1f; 机器学习是使计算机像人类一样学习与行动的科学&#xff0c;并通过观察与现实世界交互的形式向计算机提供数据和信息&#xff0c;从而随着时间的推移以自主的方式改善其学习。 通过经验提高某些任务性能的计算机程序。 人工智能>机器…

FreeRTOS ~(六)信号量 ~ (1/3)信号量解决同步缺陷

前情提要 FreeRTOS ~&#xff08;四&#xff09;同步互斥与通信 ~ &#xff08;1/3&#xff09;同步的缺陷 FreeRTOS ~&#xff08;五&#xff09;队列的常规使用 ~ &#xff08;1/5&#xff09;队列解决同步缺陷 举例子说明&#xff1a;利用信号量解决前述的"同步的缺陷&…