rust FFI is a bridge for intermodulation between rust and other languages. Through FFI rust can effectively inherit the historical assets of C language. This issue uses a few examples to talk about the specific steps of rust and C language interaction.
Scenario 1 calls C code
create project
cargo new --bin ffi_sample
Cargo.toml configuration
[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"
Write a simple c program sample.c
int add(int a,int b){
return a+b;
}
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);
}
fn main() {
cc::Build::new().file("sample.c").compile("sample");
}
code directory tree
.
├── Cargo.lock
├── Cargo.toml
├── build.rs
├── sample.c
└── src
└── main.rs
cargo run
Scenario 2 Use bindgen to bind the C language dynamic link library through the header file
Modify Cargo.toml, add bindgen dependency
[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"
Add sample.h header file
#ifndef ADD_H
#define ADD_H
int add(int a, int b);
#endif
New wrapper.h header file The wrapper.h file will include all the various header files that contain the declarations of the structures and functions we want to bind
#include "sample.h";
Rewrite build.rs and compile sample.c to generate dynamic link library sample.so ; generate rust binding c code through bindgen and output it to the bindings directory
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!");
}
Modify the main.rs include macro to introduce the binding of the sample dynamic link library. In the past, we don’t need the handwritten C function bindings. Look at the content of bindings/sample_bindings.rs and our handwritten function bindings are equivalent.
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);
}
code directory tree
.
├── Cargo.lock
├── Cargo.toml
├── bindings
│ └── sample_bindings.rs
├── build.rs
├── sample.c
├── sample.h
├── src
│ └── main.rs
└── wrapper.h
The complete code location of the ffi_sample project , readers can clone https://github.com/jiashiwen/wenpanrust and run it directly
cargo run -p ffi_sample
Scenario 3 encapsulates a library written in c
secp256k1 is a clib for elliptic curve calculation, which is a common algorithm in cryptography and privacy calculation. Let's take a look at how to package secp256k1 from an engineering perspective.
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 import submodule
cd wrapper_secp256k1
git submodule add https://github.com/bitcoin-core/secp256k1 wrapper_secp256k1/secp256k1_sys
Create a new bindings directory under the project to store binding files, which is at the same level as src
wrapper.h
#include "secp256k1_sys/secp256k1/include/secp256k1.h"
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 via
write test 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);
}
}
}
Run the test cargo test and report an error
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
The error shows that the corresponding library for compiling secp256k1 cannot be found.
Manually compile secp256k1
cd secp256k1_sys
./autogen.sh
./configure
make
make install
After the compilation is complete, the test passes
In fact, secp256k1 has a corresponding rust wrapper , we just show the encapsulation process here.
The complete code location of the wrapper_secp256k1 project , interested friends can clone https://github.com/jiashiwen/wenpanrust. Check the running results by doing the following:
-
clone project
git clone https://github.com/jiashiwen/wenpanrust cd wenpanrust
-
update submodule
git submodule init git submodule update
-
compile secp256k1
cd wrapper_secp256k1/secp256k1_sys ./autogen.sh ./configure make make install
-
run test
cargo test -p wrapper_secp256k1
References
Rust FFI (C vs Rust) learning miscellaneous notes.pdf
bindgen official document
Rust FFI programming - bindgen usage examples
Graduates of the National People’s University stole the information of all students in the school to build a beauty scoring website, and have been criminally detained. The new Windows version of QQ based on the NT architecture is officially released. The United States will restrict China’s use of Amazon, Microsoft and other cloud services that provide training AI models . Open source projects announced to stop function development LeaferJS , the highest-paid technical position in 2023, released: Visual Studio Code 1.80, an open source and powerful 2D graphics library , supports terminal image functions . The number of Threads registrations has exceeded 30 million. "Change" deepin adopts Asahi Linux to adapt to Apple M1 database ranking in July: Oracle surges, opening up the score againAuthor: JD.com Jia Shiwen
Source: JD Cloud Developer Community