Wenpan Rust -- A Taste of FFI | JD Cloud Technical Team

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;
}




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");
}





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"




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 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

Author: JD.com Jia Shiwen

Source: JD Cloud Developer Community

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 again
{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/4090830/blog/10086513