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.
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
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
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.
Like NewCIF but with an explicit ABI. Use when calling functions with non-default calling conventions, e.g. faStdcall on 32-bit Windows.
Release the memory allocated for a CIF. Call once per NewCIF / NewCIFEx when the CIF is no longer needed.
Calling functions
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.
Convenience wrapper for calling zero-argument functions. Equivalent to Call(CIF, FuncPtr, []).
Dynamic library loading
Load a shared library by file path. Returns an opaque handle, or 0 on failure. Check GetLibError on failure.
Resolve a symbol (function or variable) from a loaded library by name. Returns a pointer as an integer, or 0 if not found.
Unload a previously loaded library and free its resources. Do not use any function pointers obtained from it after this call.
Returns the last error message produced by LoadLib or GetSymbol. Returns an empty string if the last operation succeeded.
Value helpers
Wrap a 32-bit signed integer as a TFFI_Value with TypeKind = fftSInt32.
Wrap a 64-bit signed integer. Use this for size_t, ptrdiff_t, and other 64-bit C types.
Wrap a 64-bit floating-point value (fftDouble).
Wrap a 32-bit floating-point value (fftFloat).
Wrap a raw pointer (as integer) for passing to functions that expect pointer arguments (fftPointer).
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)
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.
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).
Free the closure and release the executable trampoline memory. Ensure C code is done with the pointer before calling this.
Struct 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
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 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
libffi-devPackage Info
Powers
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