RustBLR

QR code

https://talks.dhruvin.dev/rustblr

What is this book

This book contains slides and links of talks I presented at Rust Bangalore.

Each top level chapter is a subject of talk-series, and each chapter below is a subject of talk.

Slides alone may not be sufficient to understand the subject in question as they are created to accompany the presentation. There are references at the end of most slides that expand further into the subject.

Why mdbook

mdbook was chosen as it allows syntax highlighting, editable examples that run on rust playground, and testing if the examples compile and/or run correctly. It also allows me to add corrections or extensions later.

Licenses

  • Code: MIT or Apache-2.0
  • Prose: CC-BY-SA-4.0

Rust FFI

Origin story

Have you ever wondered how Rust can interoperate with other programming languages? Me neither! Well, that was until I worked on a pluggable authentication module that needed to talk to libpam.

What to expect

The talk-series explores Rust FFI (Foreign Function Interface) ecosystem. FFI allows Rust code to interoperate with code from various programming languages that speak well defined ABI (Application Binary Interface).

The talk-series assumes familiarity with Rust functions, types, and statics. It will mostly present unsafe code, and ways to make safe(r) abstractions. It is intended for Rust library authors.

External Blocks

  • Part of the Rust language
  • Foundational to Rust's FFI story
  • Provide declarations of items that are not defined in the current crate
    • functions
    • statics

Examples

extern "C" {
    // functions and statics go here
}

References

Functions

  • Patterns in parameters are disallowed
  • Must not have body

Examples

use std::ffi::*;

extern "C" {
    /// writes the string s and a trailing newline to stdout
    fn puts(s: *const c_char) -> c_int;
}

fn main() {
    let s = CString::new("Hello, RustBLR!").unwrap();
    assert!(unsafe { puts(s.as_ptr()) } >= 0);
}

References

Statics

  • Declared without the initializer expression
  • Both immutable and mutable access are unsafe as it may have uninitialized or invalid value

Examples

use std::ffi::*;

extern "C" {
    /// index of the next element to be processed in argv by getopt
    static optind: c_int;
}

fn main() {
    assert_eq!(unsafe { optind }, 1);
}

Caveats

  • An immutable static must be initialized before any Rust code is executed

References

ABI

Cross-platform ABIs

  • extern "Rust": Default for regular rust function declarations
  • extern "C": Default for extern block function declarations
  • extern "system": "C", but for Win32 on x86_32 it's stdcall

Platform-specific ABIs

Non-exhaustive list:

  • extern "cdecl"
  • extern "stdcall"
  • extern "win64"
  • extern "sysv64"
  • extern "aapcs"
  • extern "fastcall"
  • extern "vectorcall"
  • extern "thiscall"
  • extern "efiapi"

Examples

// Same as extern "C" { }
extern { }

References

Variadic Functions

  • Declared variadic by specifying ... as the last argument

Examples

use std::ffi::*;

extern "C" {
    /// print formatted output
    fn printf(format: *const c_char, ...) -> c_int;
}

fn main() {
    let format = CString::new("Hello, %s!").unwrap();
    let arg = CString::new("RustBLR").unwrap();
    assert_eq!(unsafe { printf(format.as_ptr(), arg.as_ptr()) }, 15);
}

Caveats

  • Needs least one parameter before variadic parameter
  • Normal Rust functions can not be variadic

References

Link

  • Attributes that control the behaviour of a external block
    • link
      • name: required if kind is specified
      • kind
        • dylib: default
        • static
        • framework: macos
        • raw-dylib: windows
      • modifiers
        • +bundle: static
        • -whole-archive: static
        • -verbatim
      • wasm_import_module: env
      • import_name_type: windows
    • link_name
    • link_ordinal: windows

Examples

use std::ffi::*;

#[link(name = "c", kind = "dylib")]
extern "C" {
    #[link_name = "puts"]
    fn log(s: *const c_char);
}

#[cfg(target_family = "wasm")]
#[link(wasm_import_module = "foo")]
extern {
    // ...
}

fn main() {
    let s = CString::new("Hello, RustBLR!").unwrap();
    unsafe {
        log(s.as_ptr());
    }
}

References

Bindgen

  • Maintained by rust-lang
  • De facto interface
  • Partial support for C++ and Objective C
  • Uses libclang internally

Examples

extern crate bindgen;
fn example() {
bindgen::builder()
    .header("wrapper.h")
    .generate()
    .expect("could not generate bindings");
}

References

Build Script

  • use platform- and architecture-specific headers
  • cargo install --build bindgen
  • wrapper.h
    • #include
    • replacements
  • build.rs
    • .parse_callbacks(Box::new(bindgen::CargoCallbacks))
  • include!(concat!(env!("OUT_DIR"), "/bindings.rs"))
  • #![allow(<lint>)]

References

Header

  • header
  • header_contents

Examples

extern crate bindgen;
bindgen::builder()
    .header("wrapper.h");
extern crate bindgen;
bindgen::builder()
    .header_contents("wrapper.h", "#include <lib.h>");

References

Filter

Allow/Block

  • kinds
    • file
    • function
    • type
    • var
    • item
  • allowlist_*
    • recursively
  • blocklist_*

CodegenConfig

  • with_codegen_config
    • functions
      • ignore_functions
    • types
    • vars
    • methods
      • ignore_methods
    • constructors
    • destructors

Examples

extern crate bindgen;
bindgen::builder()
    .header_contents("wrapper.h", "#include <time.h>")
    .allowlist_function("time");
/* automatically generated by rust-bindgen 0.69.4 */

pub type time_t = ::std::os::raw::c_long;
extern "C" {
    pub fn time(arg1: *mut time_t) -> time_t;
}

extern crate bindgen;
bindgen::builder()
    .header_contents("wrapper.h", "#include <getopt.h>")
    .with_codegen_config(bindgen::CodegenConfig::VARS);
/* automatically generated by rust-bindgen 0.69.4 */

pub const no_argument: u32 = 0;
pub const required_argument: u32 = 1;
pub const optional_argument: u32 = 2;
extern "C" {
    pub static mut optarg: *mut ::std::os::raw::c_char;
}
extern "C" {
    pub static mut optind: ::std::os::raw::c_int;
}
extern "C" {
    pub static mut opterr: ::std::os::raw::c_int;
}
extern "C" {
    pub static mut optopt: ::std::os::raw::c_int;
}
extern "C" {
    pub static mut optreset: ::std::os::raw::c_int;
}

References

Derive

  • derive_copy
    • no_copy
  • derive_debug
    • impl_debug
    • no_debug
  • derive_default
    • no_default
  • derive_hash
    • no_hash
  • derive_partialord
  • derive_ord
  • derive_partialeq
    • impl_partialeq
    • no_partialeq
  • derive_eq

Examples

extern crate bindgen;
bindgen::builder()
    .header_contents("wrapper.h", "#include <time.h>")
    .derive_default(true)
    .allowlist_type("timespec");
/* automatically generated by rust-bindgen 0.69.4 */

pub type time_t = ::std::os::raw::c_long;
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct timespec {
    pub tv_sec: time_t,
    pub tv_nsec: ::std::os::raw::c_long,
}

// <layout_tests>

References

Callbacks

  • rerun-if-env-changed for used env variables
    • SOURCE_DATE_EPOCH
  • rerun-if-changed for included header files
    • wrapper.h

Examples

extern crate bindgen;
bindgen::builder()
    .header("wrapper.h")
    .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()));
cargo:rerun-if-env-changed=TARGET
cargo:rerun-if-env-changed=BINDGEN_EXTRA_CLANG_ARGS
cargo:rerun-if-changed=wrapper.h

References

Include

  • include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

Examples

#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

References

Dhruvin Gandhi

QR code

https://dhruvin.dev