libffi clib FFI / Interop

Foreign Function Interface — call any C function at runtime without compile-time knowledge of its signature. No headers. No bindings. Just describe, prepare, and call.

ppm install libffi

What is libffi?

libffi is a Foreign Function Interface library that lets you call C functions at runtime without compile-time knowledge of their signatures. Instead of importing a header, you describe the function signature in code, prepare a Call Interface (CIF), then invoke any function pointer with arbitrary argument lists.

This makes it the foundation of dynamic language bridges and late-binding interop layers across the industry: Python ctypes, Ruby FFI, LuaJIT FFI, OpenJDK Panama, and .NET P/Invoke all rely on libffi under the hood.

With libffi in PascalAI you can load any shared library at runtime, resolve a symbol by name, describe its argument and return types using TFFI_Type values, and call it — all without writing a single C binding or recompiling your program.

CLib package

Requires libffi-dev. Ubuntu/Debian: sudo apt install libffi-dev. Backed by libpai_ffi.so. Supports x86, x86_64, ARM, ARM64, RISC-V, MIPS, PowerPC, SPARC.

How it works

Three-step model

1. Describe — specify the argument types and return type using TFFI_Type constants.
2. Prepare — call NewCIF to build a Call Interface handle that encodes the ABI-level calling convention.
3. Call — pass argument values wrapped in TFFI_Value records and invoke any function pointer. No recompilation needed.

Types

TFFI_CIF

An opaque handle representing a prepared Call Interface. Created by NewCIF / NewCIFEx, freed by FreeCIF. A single CIF can be reused for many calls to the same function signature.

TFFI_Closure

A callback trampoline handle. Created by NewClosure, it produces a C-callable function pointer (via GetClosurePtr) that, when invoked by C code, routes the call back into a PascalAI callback identified by a string ID.

TFFI_Type

Enum describing a C type for argument or return type declarations:

type TFFI_Type = (
  fftVoid    = 0,   { void return }
  fftInt     = 1,   { C int }
  fftFloat   = 2,   { C float }
  fftDouble  = 3,   { C double }
  fftUInt8   = 5,   fftSInt8   = 6,
  fftUInt16  = 7,   fftSInt16  = 8,
  fftUInt32  = 9,   fftSInt32  = 10,
  fftUInt64  = 11,  fftSInt64  = 12,
  fftPointer = 14,  { pointer-sized integer }
  fftStruct  = 13   { composite, see NewStructType }
);

TFFI_ABI

Selects the calling convention. Use faDefault for the platform default (recommended). Use NewCIFEx to specify an explicit ABI for cross-ABI calls on Windows.

type TFFI_ABI = (
  faDefault = 0,  { platform default }
  faSysV    = 1,  { x86/x86_64 System V }
  faStdcall = 2,  { Windows stdcall (32-bit) }
  faCdecl   = 5,  { Windows cdecl }
  faWin64   = 1   { Windows x64 }
);

TFFI_Value

A tagged union record used to pass arguments to and receive results from Call. Construct values with the Val* helper functions rather than filling fields manually.

type TFFI_Value = record
  TypeKind : TFFI_Type;
  AsInt64  : Int64;
  AsUInt64 : Int64;
  AsDouble : Double;
  AsFloat  : Single;
  AsPtr    : Int64;   { pointer as integer }
  AsBytes  : string; { raw bytes for structs }
end;

API Reference

CIF — Call Interface

function NewCIF(RetType: TFFI_Type; const ArgTypes: array of TFFI_Type): TFFI_CIF;

Prepare a Call Interface using the platform-default ABI. RetType is the return type; ArgTypes is the ordered list of argument types. Returns an opaque CIF handle. Reusable for multiple calls.

function NewCIFEx(ABI: TFFI_ABI; RetType: TFFI_Type; const ArgTypes: array of TFFI_Type): TFFI_CIF;

Like NewCIF but with an explicit ABI. Use when calling functions with non-default calling conventions, e.g. faStdcall on 32-bit Windows.

procedure FreeCIF(CIF: TFFI_CIF);

Release the memory allocated for a CIF. Call once per NewCIF / NewCIFEx when the CIF is no longer needed.

Calling functions

function Call(CIF: TFFI_CIF; FuncPtr: Int64; const Args: array of TFFI_Value): TFFI_Value;

Invoke the function at FuncPtr using the prepared CIF and the supplied argument values. Returns the result as a TFFI_Value; read the appropriate As* field based on the return type.

function CallNoArgs(CIF: TFFI_CIF; FuncPtr: Int64): TFFI_Value;

Convenience wrapper for calling zero-argument functions. Equivalent to Call(CIF, FuncPtr, []).

Dynamic library loading

function LoadLib(const Path: string): Int64;

Load a shared library by file path. Returns an opaque handle, or 0 on failure. Check GetLibError on failure.

function GetSymbol(LibHandle: Int64; const Name: string): Int64;

Resolve a symbol (function or variable) from a loaded library by name. Returns a pointer as an integer, or 0 if not found.

procedure UnloadLib(LibHandle: Int64);

Unload a previously loaded library and free its resources. Do not use any function pointers obtained from it after this call.

function GetLibError: string;

Returns the last error message produced by LoadLib or GetSymbol. Returns an empty string if the last operation succeeded.

Value helpers

function ValInt32(V: Integer): TFFI_Value;

Wrap a 32-bit signed integer as a TFFI_Value with TypeKind = fftSInt32.

function ValInt64(V: Int64): TFFI_Value;

Wrap a 64-bit signed integer. Use this for size_t, ptrdiff_t, and other 64-bit C types.

function ValDouble(V: Double): TFFI_Value;

Wrap a 64-bit floating-point value (fftDouble).

function ValFloat(V: Single): TFFI_Value;

Wrap a 32-bit floating-point value (fftFloat).

function ValPtr(Ptr: Int64): TFFI_Value;

Wrap a raw pointer (as integer) for passing to functions that expect pointer arguments (fftPointer).

function ValString(const S: string): TFFI_Value;

Wrap a PascalAI string as a C const char* pointer value. The runtime pins the string buffer for the duration of the call. Use for functions that accept char* arguments.

Closures (callback trampolines)

function NewClosure(CIF: TFFI_CIF; const CallbackID: string): TFFI_Closure;

Create a closure that, when called as a C function, invokes the PascalAI callback registered under CallbackID. The CIF describes the signature C code will use when calling the trampoline.

function GetClosurePtr(C: TFFI_Closure): Int64;

Return the C-callable function pointer for the closure. Pass this integer to C code that expects a function pointer (e.g. a qsort comparator, a signal handler, a GUI callback).

procedure FreeClosure(C: TFFI_Closure);

Free the closure and release the executable trampoline memory. Ensure C code is done with the pointer before calling this.

Struct type

function NewStructType(const Fields: array of TFFI_Type): TFFI_Type;

Build a composite fftStruct type from an ordered list of field types. The resulting TFFI_Type value can be used in NewCIF as a return type or argument type for functions that pass structs by value.

Info

function LibFFIVersion: string;

Returns the version string of the underlying libffi C library, e.g. "3.4.4". Useful for diagnostics and compatibility checks.

Code Example

Dynamically load libc and call strlen without any C headers or compile-time bindings:

uses libffi;

var
  Lib       : Int64;
  StrlenPtr : Int64;
  CIF       : TFFI_CIF;
  Res       : TFFI_Value;
begin
  Lib := LoadLib('libc.so.6');
  if Lib = 0 then begin
    WriteLn('Failed to load libc: ', GetLibError());
    Exit;
  end;

  StrlenPtr := GetSymbol(Lib, 'strlen');

  { strlen(const char*): size_t }
  CIF := NewCIF(fftSInt64, [fftPointer]);

  Res := Call(CIF, StrlenPtr, [ValString('Hello, world!')]);
  WriteLn('strlen = ', Res.AsInt64);  { 13 }

  FreeCIF(CIF);
  UnloadLib(Lib);
end.
Closures

Closures let you create C-callable function pointers that invoke PascalAI callbacks. This is useful whenever a C library expects a function pointer argument — for example a sort comparator, an event handler, or a plugin hook. Create a closure with NewClosure, obtain its pointer with GetClosurePtr, pass the integer to the C library, and free with FreeClosure when done.

Install

ppm install libffi
Requires libffi-dev

Package Info

Version1.0.0
Typeclib
C Librarylibffi-dev
Nativelibpai_ffi.so
Authorgustavo

Powers

Python ctypes
Ruby FFI
LuaJIT FFI
OpenJDK Panama
.NET P/Invoke

Functions

  • NewCIF
  • NewCIFEx
  • FreeCIF
  • Call
  • CallNoArgs
  • LoadLib
  • GetSymbol
  • UnloadLib
  • GetLibError
  • ValInt32
  • ValInt64
  • ValDouble
  • ValFloat
  • ValPtr
  • ValString
  • NewClosure
  • GetClosurePtr
  • FreeClosure
  • NewStructType
  • LibFFIVersion

Types

  • TFFI_CIF
  • TFFI_Closure
  • TFFI_Type
  • TFFI_ABI
  • TFFI_Value

See Also