Rust-ffi-guide - A guide for doing FFI using Rust

Overview

Using unsafe for Fun and Profit

Build Status Creative Commons Zero License

A guide to traversing the FFI boundary between Rust and other languages. A rendered version is available here. This guide is centred around the idea of building a REST client using Qt (C++) for the GUI and reqwest (Rust) for the business logic.

Building

Building and viewing the book locally is really easy. First you need to get the source code:

$ git clone https://github.com/Michael-F-Bryan/rust-ffi-guide

Make sure you have mdbook installed:

$ cargo install mdbook

Then tell mdbook to build and serve the book:

$ mdbook serve --open

It should now be viewable at http://localhost:3000/ (if it didn't open up automatically).

To build the application itself you'll need the following installed:

  • qt5
  • rust (install with rustup)
  • mdbook (cargo install mdbook)

In this application we're using cmake as the build system. The ci/test.sh script will make a build/ directory and invoke cmake to compile and test everything.

$ ./ci/test.sh

The final application should now be at build/gui.

Alternatively, if you don't want to install all the dependencies I've created a docker image (michaelfbryan/ffi-guide) for compiling Rust and Qt.

$ docker run \
    -v $(pwd):/code \
    -v ~/.cargo:$HOME/.cargo \
    -e CARGO_HOME=$HOME/.cargo \
    --user $UID \
    michaelfbryan/ffi-guide ci/test.sh

Contributing

If there's anything you feel is missing or could be improved, please create an issue. Pull requests are welcome too!

Contributors

Comments
  • Simplify Primes class

    Simplify Primes class

    closes #25 closes #30 Fixes #32 I seem to prefer generators. Here are what benefits I see.

    1. StopIteration is automatically raised when Primes stops yielding
    2. Only one value is passed back at a time (memory savings)
    opened by gurgalex 12
  • Pythonic section

    Pythonic section

        def __iter__(self):
            running = True
            while running:
                prime = self.next()
                yield prime
                running = prime != 0
    

    Is there any reason to yield 0? I think, it was intended to indicate the end of sequence.

    opened by pravic 10
  • Bindgen: Removal of first person

    Bindgen: Removal of first person

    Working toward #8

    • Reworded sentences to not be in the first person.
    • Fixed a typo in the bindgen intro.
    • Major rewrite of what the build.rs script does and when.
    opened by gurgalex 9
  • Make the book more consistent

    Make the book more consistent

    Bits and pieces of this book were written in the first person, while others have been written in the third person. To the book more consistent and easier for others to contribute to, all usage of the first person point-of-view should be converted to third person.

    The easiest way to find what to fix is to grep all markdown files for common first person phrases (like " I ").

    (Hint: grep -E " I | I'm | me | my " $(find . -name '*.md'))


    Progress so far:

    opened by Michael-F-Bryan 7
  • Wrong comment char for rust code

    Wrong comment char for rust code

    ~~I'm not sure if I'm on the right place for this:~~ https://michael-f-bryan.github.io/rust-ffi-guide/fun/problems.html ~~All your examples have the following code inside:~~

    # #![allow(unused_variables)]
    #fn main() {
    

    ~~This won't ever compile as # is not a correct comment char in rust. Please correct it to // or /* or leave this part out.~~

    error: expected `[`, found `#`
     --> adder.rs:2:3
      |
    2 | # #![allow(unused_variables)]
      |   ^
    
    error: aborting due to previous error(s)
    

    I'm sorry, seems like without both JS CDN's I'm seeing some code to parse.

    And the first example uses "uit" not "uint", unknown type.

    opened by 0xpr03 6
  • First person third arrays

    First person third arrays

    Working toward #8 Each pull request will focus on one file.

    Reworded some more sentences. Although, in some cases I reworded a paragraph to reduce the amount of "you", or to make a sentence more straightforward.

    opened by gurgalex 6
  • Revised tutorial

    Revised tutorial

    This is a revised tutorial written from the perspective of someone wanting to create a basic REST client which uses Qt for the GUI and Rust for the business logic.

    (rendered)

    (fixes #40)

    opened by Michael-F-Bryan 5
  • Testing

    Testing

    This introduces a more organised scheme for building and testing the example code. Each chapter has its own Makefile which must define the following three rules: build, test, and clean. Allowing you to run make test in the top level directory and have make test be run in each chapter.

    It also adds cargo workspaces so that the examples can share build artefacts instead of unnecessarily rebuilding common crates like libc half a dozen times.

    cc: #29

    opened by Michael-F-Bryan 5
  • First person to third person pt1

    First person to third person pt1

    Partial patch set for #8 So, I divided up the commits into little pieces in case anything seems off. Also, thanks for the existing book text!

    Comments?

    opened by gurgalex 5
  • Plugin system is not supported within the rust ecosystem itself

    Plugin system is not supported within the rust ecosystem itself

    I was glad to find your article and applied it in my project. However, this does not work when passing structures around dynamic libraries.

    For now, it sounds to me that the use case is only for foreign languages with only canonical structures around that.

    See https://github.com/rust-lang/rust/issues/67179 and so https://github.com/rust-lang/rust/pull/63338 with this https://github.com/rust-lang/rust/pull/63338#issuecomment-519122090

    Do you have any workaround for this, please ? Thanks!

    opened by sidgilles 4
  • Catching *nix signals (segfault in particular)?

    Catching *nix signals (segfault in particular)?

    As part of supporting multiple versions of LLVM in inkwell, I ran into a case where perfectly valid input to a certain function for non existent data (similar to looking up a key that doesn't exist in a hash table) would cause a segfault in a small subset of versions of LLVM. This is presumably a LLVM bug since it works fine in previous versions.

    Thinking about this, I'm wondering: How terrible of an idea is it to set up a signal handler(IE via the signal crate) to catch that segfault and return the error case that I would have normally returned in LLVM versions that don't segfault?

    I'm thinking at best the segfault could just be a null pointer dereference, but in the worst case it could have done something crazy like corrupted the whole stack...

    Is this a crazy idea? What if I first look at the LLVM source code and am able to verify that the bug(maybe there's a fix I can look at in a newer version)'s segfault is relatively harmless in each offending version (ie null ptr deref or reading (but not writing) to invalid memory?).

    opened by TheDan64 4
  • Regarding plugins: ABI of `dyn Trait` is unstable and using it across FFI boundary might be UB

    Regarding plugins: ABI of `dyn Trait` is unstable and using it across FFI boundary might be UB

    According to your own article (I believe), ABI for dyn Trait objects is unstable, therefore the following use in this guide might be UB:

    #[macro_export]
    macro_rules! declare_plugin {
        ($plugin_type:ty, $constructor:path) => {
            #[no_mangle]
            pub extern "C" fn _plugin_create() -> *mut $crate::Plugin {
                // make sure the constructor is the correct type.
                let constructor: fn() -> $plugin_type = $constructor;
    
                let object = constructor();
                let boxed: Box<$crate::Plugin> = Box::new(object);
                Box::into_raw(boxed)
            }
        };
    }
    

    Note the use of *mut $crate::Plugin as the return type. In Rust 2018/2021, it should be *mut dyn $crate::Plugin because Plugin is a trait (instead of a type). And if the layout of trait objects were to change for building the plugin and the host program (though I doubt if this would ever happen), this would result in UB.

    For solutions, it seems that abi_stable::sabi_trait could be a candidate, but it looks ad-hoc and pulls in a non-trivial amount of dependencies. Another way would be to just use the "manual vtable" approach mentioned in your blog, which requires some boilerplate, but looks good otherwise.

    opened by ruifengx 0
  • The

    The "Dynamic Loading & Plugins" chapter example code causes a segfault on Windows

    The chapter gives this example of loading a plugin:

    pub unsafe fn load_plugin<P: AsRef<OsStr>>(&mut self, filename: P) -> Result<()> {
        type PluginCreate = unsafe fn() -> *mut Plugin;
    
        let lib = Library::new(filename.as_ref()).chain_err(|| "Unable to load the plugin")?;
    
        // We need to keep the library around otherwise our plugin's vtable will
        // point to garbage. We do this little dance to make sure the library
        // doesn't end up getting moved.
        self.loaded_libraries.push(lib);
    
        let lib = self.loaded_libraries.last().unwrap();
    
        let constructor: Symbol<PluginCreate> = lib.get(b"_plugin_create")
            .chain_err(|| "The `_plugin_create` symbol wasn't found.")?;
        let boxed_raw = constructor();
    
        let plugin = Box::from_raw(boxed_raw);
        debug!("Loaded plugin: {}", plugin.name());
        plugin.on_plugin_load();
        self.plugins.push(plugin);
    
    
        Ok(())
    }
    

    However (in my testing, which has only been on Windows 10), this code crashes unless I change the type alias to:

    type PluginCreate = unsafe extern "C" fn() -> *mut Plugin;
    

    With the addition of extern "C", everything appears to work correctly.

    opened by zrneely 1
  • Guide Rewrite

    Guide Rewrite

    It's been a while since I last updated the guide and I've learned a lot since then. I think it may be a good idea to start over and take a slightly different approach.

    I think we may want to break things into two parts. In the first part we'll address common patterns and techniques used when writing FFI code, with the second part being a worked example (or two?) that lets us show off these techniques.

    Progress so far (rendered)


    Concepts and Patterns:

    • [x] Calling basic functions and linking (#[no_mangle], extern fn ... and all that) (#66)
    • [x] Passing around reference types (&[u8], char*, etc) (#69)
    • [x] Binding to complex objects (#70)
    • [ ] Ownership and memory safety (e.g. who should call free and what happens if you free from the wrong allocator)
    • [x] Callbacks and function pointers
    • [ ] Proper error handling
    • [ ] Best practices for API design
    • [ ] Starting a task in the background
    • [ ] Exception safety
    • [x] Dynamic loading and plugins (dlopen/LoadLibrary) (#71)
    • [x] Fun with build systems (e.g. build a Rust crate using cmake)
    • [ ] Bindings
      • [ ] Writing a C++ wrapper class for a Rust type

    Ideas for worked examples:

    • [x] Write a Rust wrapper for a common C library (#67)
    • [ ] Use a Rust crate as part of a Qt (C++) application (e.g. use reqwest to do HTTP requests in the background)
    • [ ] Using a Rust library to speed up a Python application
    • [ ] Embedding Node.JS in a Rust application so we can call some javascript stuff

    Note: All PRs should be made against the reboot branch (#65) so we don't overwrite the existing guide until the new version is ready.

    opened by Michael-F-Bryan 12
  • Rust Enums and Tagged Unions

    Rust Enums and Tagged Unions

    This problem would pass a normal Rust enum to C++, where the C++ code has declared a tagged union which roughly matches how Rust does things internally.

    The solution should mention that this is UB because an enum's internal representation is unspecified. Instead Rust should manually define a tagged union which is the equivalent of our enum, possibly adding the appropriate From<...> impls.

    You'd need to explicitly write something like this:

    #[derive(Copy, Clone)]
    #[repr(C)]
    struct Car {
        tag: CarType,
        value: CarValue,
    }
    
    #[derive(Copy, Clone)]
    #[repr(C)]
    union CarValue {
        fast: FastCar,
        slow: SlowCar,
    }
    
    #[derive(Copy, Clone, Debug)]
    struct FastCar(u32);
    
    #[derive(Copy, Clone, Debug)]
    struct SlowCar(f64);
    
    #[derive(Copy, Clone)]
    #[repr(C)]
    enum CarType {
        Fast,
        Slow,
    }
    

    As well as its C equivalent:

    struct SlowCar{
      ...
    }
    
    struct FastCar{
      ...
    }
    
    union union_car {
      FastCar,
      SlowCar
    }
    
    enum Cartype{Slow,Fast};
    
    struct Car{
      union_car inner;
      Cartype tag;
    } 
    
    opened by Michael-F-Bryan 0
  • Beware of allocators mismatch

    Beware of allocators mismatch

    When eg. you get a buffer allocated from the C side of the FFI and are expected to manage its lifetime in Rust, extra care needs to happen in the Rust code to deallocate with the C malloc/free and not just drop it since Rust is using jemalloc.

    The same precautions need to be taken when handing buffers to the C side.

    I've found it needed to write small custom functions to expose deallocation or allocation missing on the C side to make that clear and correct.

    opened by fabricedesre 2
Rust in Haskell FFI Example

Provides an example for using Rust in Haskell. To use this you'll need cargo, rustc, cabal and GHC installed. To execute the example run the following

Michael Gattozzi 21 Oct 1, 2022
A safe Rust FFI binding for the NVIDIA® Tools Extension SDK (NVTX).

NVIDIA® Tools Extension SDK (NVTX) is a C-based Application Programming Interface (API) for annotating events, code ranges, and resources in your applications. Official documentation for NVIDIA®'s NVTX can be found here.

Spencer Imbleau 78 Jan 2, 2023
Rust Attribute-Based Encryption library rabe's C FFI binding , support CP-ABE and KP-ABE encrypt and decrypt, submodule of Rabe.Core c# library.

Rabe-ffi Rust Attribute-Based Encryption library rabe's C FFI binding , support CP-ABE and KP-ABE encrypt and decrypt, submodule of Rabe.Core c# libra

Aya0wind 2 Oct 10, 2022
Handy macro to generate C-FFI bindings to Rust for Haskell

hs-bindgen Handy macro to generate C-FFI bindings to Rust for Haskell. This library intended to work best in a project configured by cargo-cabal. N.B.

Yvan Sraka 32 Feb 16, 2023
libc - Raw FFI bindings to platforms' system libraries

libc provides all of the definitions necessary to easily interoperate with C code (or "C-like" code) on each of the platforms that Rust supports. This includes type definitions (e.g. c_int), constants (e.g. EINVAL) as well as function headers (e.g. malloc).

The Rust Programming Language 1.5k Jan 1, 2023
A cross-platform crate with FFI bindings to allow for complex vehicle ECU diagnostics.

ecu_diagnostics A cross-platform crate with FFI bindings to allow for complex vehicle ECU diagnostics. IMPORTANT Currently this crate is not 100% read

Ashcon Mohseninia 80 Dec 24, 2022
Easy way to write Node.js module using Rust

node-bindgen Easy way to write native Node.js module using idiomatic Rust Features Easy: Just write idiomatic Rust code, node-bindgen take care of gen

InfinyOn 346 Jan 3, 2023
Safe Rust <---> GraalVM Polyglot bindings using procedural macros

The class macro is the primary way to generate bindings to Java types; it will generate a struct (with generics if specified) that implements Pass and Receive and has all the methods you give stubs for. The methods generated can be used like normal rust methods, however mutability is not enforced. The fully-qualified type name should precede a block containing method and constructor stubs. Java primitives like char, int, and byte are aliased to corresponding Rust types.

Alec Petridis 33 Dec 28, 2022
Rust Lambda using Serverless

Rust Serverless Lambda Template

섹스 신청서 4 May 31, 2022
A Web-App written in Rust with Yew, using the same SyntaxHighlighter from Google Code Archive as planetb.ca

PlanetB SyntaxHighlighter About This is a small app, providing static files to have a frontend to format your code so you can paste it with styles to

Christof Weickhardt 2 Dec 14, 2022
Parametric surfaces drawn using the Rust + WASM toolchain with WebGL, React, and TypeScript.

Parametric Surfaces in the Browser My.Movie.3.mp4 Wanted to experiment with WebGL using the Rust + WASM toolchain, with React and TypeScript to glue e

Benji Nguyen 45 Oct 21, 2022
A collection of unsound rust functions using entirly *safe* code

A collection of unsound rust functions using entirly *safe* code

null 2 Sep 6, 2022
Realtime audio processing / synthesis using Rust/WASM in the browser.

Rust Audio About This repo is my investigation into using Rust for creative audio coding on various platforms (e.g. desktop, web, etc.), but especiall

Austin Theriot 30 Jan 5, 2023
call rest api using rust + yew

Call Rest API With Rust and Yew: USA Weather Service API Open Data YEW Complete Tutorial YouTube Video https://youtu.be/dSJULWtd3y0 How to run trunk s

Security Union 8 Dec 23, 2022
Lua bytecode parser written in Rust using nom, part of metaworm's lua decompiler

luac-parser (中文) lua字节码解析器, 目前支持 lua51, lua53, lua54 这是目前效果最好的lua反编译器 metaworm's luadec 的一部分 可以基于此代码定制你所需的lua字节码解析器,编译成WASM,让metaworm's luadec加载使用,来反编

metaworm 4 Mar 16, 2023
Freeze.rs is a payload toolkit for bypassing EDRs using suspended processes, direct syscalls written in RUST

Freeze.rs More Information If you want to learn more about the techniques utilized in this framework, please take a look at SourceZero Blog and the or

Optiv Security 385 May 9, 2023
plugy empowers you to construct agnostic dynamic plugin systems using Rust and WebAssembly.

plugy plugy is a plugin system designed to enable the seamless integration of Rust-based plugins into your application. It provides a runtime environm

Geoffrey Mureithi 22 Aug 12, 2023
Lisp but using indentations

Calcit Runner An interpreter for Calcit snapshot file. Home http://calcit-lang.org/ APIs http://apis.calcit-lang.org/ Running Calcit Editor with compa

Calcit 63 Dec 26, 2022
Python bindings for akinator-rs using pyo3

Akinator-py python bindings for akinator-rs using pyo3 Installation Prebuilt wheels are uploaded onto pypi, if you platform is supported, you can inst

Tom-the-Bomb 4 Nov 17, 2022