# FFI in rust @wusyong, 吳昱緯 ---- * Rust ABI is not stable. * Rust supports the platform-ABI (see [External blocks](https://doc.rust-lang.org/reference/items/external-blocks.html)) * Effecient C bindings --- ## Using C from Rust ---- ### printf from C 假設我們真的很想用 printf ---- ```c #include "hello.h" #include <stdio.h> uint32_t printf_wrapper(const char *str) { printf("%s\n", str); return 0; } ``` ```c #include <stdint.h> uint32_t printf_wrapper(const char *str); ``` ---- - Bind functions in header - Link the library - Call them in `unsafe` - Pass parameter to C functions ---- ### Disable Rust naming lints ```rust #![allow(non_camel_case_types)] #![allow(non_upper_case_globals)] #![allow(non_snake_case)] ``` ---- ```c #include <stdint.h> uint32_t printf_wrapper(const char *str); ``` ```rust use std::os::raw::{c_uint, c_char}; extern "C" { printf_wrapper(strr: const *c_char) -> c_uint; } ``` ---- ### Primitive types `std::os::raw` & `std::ffi` - c_uint ↔ u32 - c_int ↔ i32 - c_void ↔ () - CString, Cstr, ... ---- ```rust use std::os::raw::{c_uint, c_char}; use std::ffi::CStr; extern "C" { fn printf_wrapper(strr: const *c_char) -> c_uint; } fn main() { unsafe { let my_strr = "Hello World!"; let _: u32 = printf_wrapper(CStr::from(my_strr).unwrap()); } } ``` ---- ### Structs `#[repr(C)]` directs the compiler to use the platform-layout. ```rust #[repr(C)] pub struct myapp_t { id: u32, other_type: Box<myother_t>, initialised: bool, } ``` ---- ### Enum ```rust #[repr(C)] pub enum FakeBool { No = 0, OhYea = 1 } ``` ---- ### Opaque type When not knowing (or caring) about internal layout, opaque structs can be used. ```rust #[repr(C)] pub struct myopaque_t { _priv: [u8; 0] } ``` ---- ### Callbacks ```rust extern "C" { pub fn register_callback( state: *mut c_void, name: *const c_char, cb: extern "C" fn(state: *mut c_void) -> *const c_uint, ); } ``` ---- ### Cargo (build-system) support * Build native code via build-dependency crates: * gcc, clang, cmake, ... * `build.rs` file responsible for linking code * More details on [Build Scripts](https://doc.rust-lang.org/cargo/reference/build-scripts.html) ---- ``` [build-dependencies] cmake = "0.1" ``` ```rust use cmake::Config; fn main() { let mut dst = Config::new("mimalloc") .define("MI_OVERRIDE", "OFF") .define("MI_SECURE", "OFF") .build_target("mimalloc-static") .build(); dst.push("./build"); let out_name = "mimalloc"; println!("cargo:rustc-link-search=native={}", dst.display()); println!("cargo:rustc-link-lib=static={}", out_name); } ``` --- ## Real Example Binding mimalloc to rust! 常用的函式庫通常都有人弄好了 ---- ![](https://i.imgur.com/Hd8SMQa.png) ---- ### [bindgen](https://rust-lang.github.io/rust-bindgen/) Instead of write all `extern "C"` block yourelf, generate them! ---- ```rust // Continue in build.rs ... let bindings = bindgen::Builder::default() .header("mimalloc/include/mimalloc.h") .generate() .expect("Unable to generate bindings"); let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); bindings .write_to_file(out_path.join("bindings.rs")) .expect("Couldn't write bindings!"); ``` ---- ```rust // In src/lib.rs #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] include!(concat!(env!("OUT_DIR"), "/bindings.rs")); ``` --- ### Building Layers ```rust pub struct MiMalloc; unsafe impl GlobalAlloc for MiMalloc { #[inline] unsafe fn alloc(&self, layout: Layout) -> *mut u8 { if Self::is_aligned(&layout) { mi_malloc(layout.size()) as *mut u8 } else { mi_malloc_aligned(layout.size(), layout.align()) as *mut u8 } } // ... } ``` ---- ### Memory management Implement `Drop` trait for your instance in most cases ```rust #[inline] unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { mi_free(ptr as *mut _); } ``` --- ## Using Rust from C Use platform-ABI in reverse – emit a native library ---- ### Similar concepts * extern "C" functions. * `#[repr(C)]` structures/ enums. * Making data C compatible. * Panic is undefine behavior! Use `catch_unwind` ---- ```rust use std::panic::catch_unwind; #[no_mangle] pub extern fn hello_rust() -> *const u8 { "Hello, world!\0".as_ptr() } #[no_mangle] pub extern fn oh_no() -> i32 { let result = catch_unwind(|| { panic!("Oops!"); }); match result { Ok(_) => 0, Err(_) => 1, } } ``` ---- ### Cargo settings [Building dynamic or static libraries](https://doc.rust-lang.org/cargo/reference/manifest.html#building-dynamic-or-static-libraries) ``` [lib] name = "connector" crate-type = ["cdylib"] ``` ---- ### Creating bindings ###### (No, this is not a deja-vu) C code needs .h files to include – define Rust functions there. ---- ### [cbindgen](https://github.com/eqrion/cbindgen) - great tool to auto-generate .h files - Use as an application with toml configuration - Or just build with build script --- ## Reference [The Reference](https://doc.rust-lang.org/reference/items/external-blocks.html) (duh) [The Rustonomicon](https://doc.rust-lang.org/nomicon/ffi.html) [Making a `*-sys` crate](https://kornel.ski/rust-sys-crate)
{"metaMigratedAt":"2023-06-15T00:54:39.520Z","metaMigratedFrom":"YAML","title":"FFI","breaks":true,"description":"FFI","contributors":"[{\"id\":\"9913fcec-c953-46f3-8b1c-33170571dfd1\",\"add\":7594,\"del\":2167}]"}
    1308 views