Generating Rust Bindings for Embedded Libraries

As I talked about in my last post, Embedded Rust Right Now!, you can call C functions directly from Rust without much difficulty. However, you normally still need to provide Rust types and prototypes for the corresponding C types and functions you want to use. This can be a time-consuming and error-prone process.

Fortunately there is a tool call rust-bindgen that can generate bindings automatically directly from C header files! It’s a little trickier when you’re cross compiling to target embedded systems, but you just need to pass some extra clang flags to bindgen.

Setting up rust-bindgen

First off, you’ll need a Rust nightly (www.rust-lang.org/install.html). Then grab Rust bindgen (https://github.com/servo/rust-bindgen) and follow the instructions in the README to build it:

cargo build

The bindgen executable will be at: ./target/debug/bindgen

You’ll also need to add some of the clang libraries to your DYLD_LIBRARY_PATH:

export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/

Generating Bindings!

Now you’re ready to generate bindings. As an example, I’m going make Rust bindings for the STM32F4 HAL and driver library STM32Cube.

The main header file is stm32f4xx_hal.h, so the basic command to start with would be bindgen stm32f4xx_hal.h. Unfortunately we’re going to need to add a few more flags to make that work.

I added some include paths for the headers that stm32f4xx_hal.h will refer to:

-ISTM32CubeFWF4V1.4.0/Drivers/CMSIS/Device/ST/STM32F4xx/Include -ISTM32CubeFWF4V1.4.0/Drivers/CMSIS/Include

and an extra option also needed by stm32f4xx_hal.h

-DSTM32F439xx

Making Sure our Types Match our Target

At this point the binding generation would “work” but would be tailored for your host system instead of the STM32F4 target. In particular, certain types might end up being the wrong size (32 vs 64bit, etc.).

To fix this, we need to add flags for a sysroot for our target platform. I’m using this one: launchpad.net/gcc-arm-embedded.

–sysroot=gcc-arm-none-eabi-4_9-2014q4/arm-none-eabi

And then flags to specify the target architecture/CPU:

-target thumbv7em-none-eabi -mcpu=cortex-m4 -mthumb -mfloat-abi=soft

The final command ends up being:

bindgen STM32Cube_FW_F4_V1.4.0/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal.h -DSTM32F439xx -ISTM32Cube_FW_F4_V1.4.0/Drivers/CMSIS/Device/ST/STM32F4xx/Include/ -I../STM32Cube_FW_F4_V1.4.0/Drivers/CMSIS/Include –sysroot=gcc-arm-none-eabi-4_9-2014q4/arm-none-eabi -target thumbv7em-none-eabi -mcpu=cortex-m4 -mthumb -mfloat-abi=soft -o stm32f4xx_hal.rs

And now we have Rust bindings for STM32F4Cube in stm32f4xx_hal.rs!

If you’re using no_std, to build without the Rust standard library you’ll have to replace instances of ::std:: in stm32f4xx_hal.rs with ::core::. Also, if you don’t have a Rust libc built for your target you can replace instances of types like ::libc::c_int or ::libc::c_short with the equivalent Rust type for your target such as i32 and i16 to remove the dependency on libc. Other than that you should be set.

Conversation
  • Ben Gesoff says:

    Is there a reason for not compiling bindgen with the –release flag?

    • Job Vranish Job Vranish says:

      Not that I know of, I would expect it would still work just fine.

  • Emily says:

    SIP – A Tool for Generating Python Bindings for C and …

  • Ildar says:

    Doesn’t work anymore (it seems):
    error: unions are unstable and possibly buggy (see issue #32836)

  • Comments are closed.