Using FFI, dylib or DSos to generate or resue some code

2025-09-16
, ,

Motivation: It’s better then just using the latter from someone else without any clarity of, how any of that is going to for my purpose.

Rational

The src of this blog uses #[no_mangle] and pub extern "C" in Rust code ,w hic is compiled as a dylib and generate the WebAssembly, js files, to be used for frontend.

Doing so allows multiple programs to use the same library code at runtime, reducing memory usage and improving efficiency. Necessary compiler related tools features for it can be codegen 1, bindgen 2, Foriegn function intrecae or the languages bindings.

If the langauge ,functions of which, you export and one you want to import this dynamic shared library object to, feature allows it by design, natively or within doable data marshalling and runtime overhead.

Caveat

FFI are like a blind spot in your type system. Writing them manually is both frankly painful and really dangerous, as your compiler will not warn you about non-matching interfaces. Binding generation comes to the rescue by considerably reducing the room for human errors. As a bonus, it also makes maintainers’ life easier thanks to a smaller and more readable code base.3

FFI is more doable, than just serialing complex data. While a universal serialisation format (MessagePack) is tempting for language agniostic interoperability and reducing risk of memory errors, the latter can also be done better with the value ownership model of Rust, leading to aut dealloc of memory and it adds overhead/call.

OTOH, while a traditional ffi requires careful manual marshaling of types, memory management, ABI handling and yet remains less portable and flaxible, it also means direct function calls using native calling conventions and memory representations and hence proximity to metal.

Illustrations

Raw code insertion

/* a wrapper to Eval.hs */
#include <HsFFI.h>

static void my_enter(void) __attribute__((constructor));
static void my_enter(void)
{
  static char *argv[] = { "libEval.so", 0 }, **argv_ = argv;
  static int argc = 1;
  hs_init(&argc, &argv_);
}

static void my_exit(void) __attribute__((destructor));
static void my_exit(void)
{
  hs_exit();
}

Footnotes


  1. create bindings that allow programs written in one programming language to call functions or use services from another language. It involves automatically generating the necessary code to interface with external libraries, making it easier to integrate native code into applications.↩︎

  2. automatically generate Rust bindings for C and C++ libraries, allowing Rust code to call functions and use types defined in C/C++ headers. This simplifies the process of integrating existing C/C++ libraries into Rust projects without needing to rewrite them.↩︎

  3. https://engineering.iog.io/2023-01-26-hs-bindgen-introduction/↩︎