Dynamic FFI
(require dynamic-ffi/unsafe) | package: dynamic-ffi |
1 Warning: Read this Section Before Use
Like Racket’s built-in ffi/unsafe module, this library allows Racket to use C functions and data structures that are not memory-managed by Racket. Racket cannot provide safety guarantees by default for FFI objects created using this library. Users of this library will be required to self-enforce memory management in C and/or manually expose FFI objects to Racket’s garbage collector. Extra care should be taken to ensure that C functions bound via this library or Racket’s built-in FFI do not contain errors like buffer overflows, which could corrupt the runtime, cause undefined behavior, and prevent Racket from providing useful error messages.
Users of this library should be extremely cautious. The library makes it easier to call C functions from Racket, but does not make it safer.
Finally:
Passing unsanitized user-input to any function in this library is horribly dangerous. You should never do it under any circumstance.
2 Dependencies
Dynamic FFI requires clang and llvm headers and libraries to work. Clang versions 6 and 7 are both known to work with Dynamic FFI. This library does not bundle clang or llvm.
To install a clang toolchain on Fedora:
sudo dnf install "@development tools" racket llvm-devel clang-devel
To install a clang toolchain on Ubuntu:
sudo apt-get install "build-essential" racket llvm-dev libclang-dev clang
To install a clang toolchain on macOS (assuming that Homebrew is installed), run brew install llvm, then follow the instructions emitted by Homebrew to setup paths.
During the raco package install, Dynamic FFI will compile itself and link with the system clang libraries. Dependencies should be installed before the raco installation, or else the library will defer building itself, and will complain about missing dependencies when you try to use it. To finish the installation process, install the required dependencies and run the following command:
raco setup -p -D dynamic-ffi
3 Defining FFIs
syntax
(define-dynamic-ffi id lib header ...)
lib :
(or/c string? path? (cons/c (or/c string? path?) (listof string?)))
header : (or/c string? path?)
lib can be a relative library created using dynamic-ffi-lib, or a hard-coded path omitting the object file extension.
(require dynamic-ffi/unsafe) (define-dynamic-ffi libc (dynamic-ffi-lib "libc" "6") "/usr/include/stdio.h") (libc 'printf "hello world\n")
procedure
(dynamic-ffi-lib lib version ...) →
(cons/c (or/c string? path?) (listof string?)) lib : (or/c string? path?) version : string?
syntax
(define-dynamic-ffi/cached id lib header ...)
lib : (or/c string? path?)
header : (or/c string? path?)
4 Generating Static FFIs
This library has the ability to generate static FFI files that can be used without the dependency on dynamic-ffi.
procedure
(generate-static-ffi ffi-name file lib-path headers ...) → void? ffi-name : (or/c string? symbol?) file : (or/c string? path?) lib-path : (or/c string? path? (cons/c (or/c string? path?) (listof string?))) headers : (or/c string? path?)
procedure
(generate-mapped-static-ffi ffi-name file lib-path headers ...) → void? ffi-name : (or/c string? symbol?) file : (or/c string? path?) lib-path : (or/c string? path? (cons/c (or/c string? path?) (listof string?))) headers : (or/c string? path?)
5 Inline FFIs
This library allows users to write C functions inline, which will be compiled at runtime and provided as a dynamic FFI.
syntax
(define-inline-ffi name code ... [#:compiler compiler] [#:compile-flags flags])
code : string?
compiler : string?
flags : (or/c string? 'auto)
@define-inline-ffi[mylib]{ int add(int x, int y) { return x + y; } } (mylib 'add 3 4)
The form can be used without the at-reader as well.
(define-inline-ffi mylib "int add(int x, int y) {\n" " return x + y;\n" "}") (mylib 'add 3 4)
Extra compile flags can be passed to define-inline-ffi, and the default compiler can be overridden.
(define-inline-ffi libm #:compile-flags "-lm" #:compiler "clang" "#include <math.h>\n" "double square_root(double x) {\n" " return sqrt(x);\n" "}") (libm 'square_root 16)
6 Limitations
This library is able to generate dynamic FFI bindings for a fairly large portion of the C language standard. Occasionally we find special declaration types that clang treats differently, which the library may not have support for yet. When such declarations are encountered, dynamic-ffi will emit error messages with details about the missing functionality. Please report any issues you find at the project issue tracker.
As a workaround in almost every case, define-inline-ffi can be used to write a wrapper around functions that dynamic-ffi is unable to parse.
7 Acknowledgements
Special thanks to Jay McCarthy for suggesting this project idea, and for his advisership during its implementation.