LibFFI is a foreign function interface library allowing programs to arbitrarily call native function without pointers and to bind function pointers to generic functions which take variable arguments via closures. It is used to bind native code in modern scripting languages.
Website: https://sourceware.org/libffi/
Platforms supported: Windows, Linux, FreeBSD, Haiku
Headers to include: ffi.bi
Header version: 3.3 declarations with x86 closure layouts for libffi 3.4 and newer
Return storage and closure ABI
Match the call interface's type to the actual function prototype. A C unsigned 32-bit return uses FreeBASIC ULong. ffi_call widens small integer results into ffi_arg storage; use that type for the output buffer. Closure callbacks must also write small integer results through an ffi_arg Ptr.
Modern x86 libffi uses 16-byte trampolines on 32-bit targets and 32-byte trampolines on 64-bit targets. This changes the closure layout from libffi 3.3. Use the corresponding modern native SDK with this binding.
Haiku x86 uses the System V ABI. The x86-64 binding has been checked against the native libffi 3.4.6 headers for structure sizes, field offsets and ABI constants, followed by actual calls and closures through the original examples.
Allocate closures through ffi_closure_alloc and release them with ffi_closure_free. Use the executable code pointer returned by the allocator, which may differ from the writable closure pointer. Keep the call interface, argument types and application data valid until the closure is no longer callable. On Win32, FreeBASIC rounds the closure storage to 32 bytes while GCC's aligned C typedef reports 28 bytes; their field offsets and alignment agree, and the native allocator accepts the extra tail storage.
Windows DLL data is imported explicitly to avoid out-of-range GAS64 relocations under ASLR. Define FFI_STATIC_BUILD before including ffi.bi when linking a static libffi archive. Installed libffi 3.7.1 DLLs and actual static archives have been checked on Win32 and Win64 against native C layouts, guarded 32-bit return values, closures and the original examples on both backends.
Example
Hello world:
'' examples/manual/libraries/ffi/helloworld.bas
''
'' Example extracted from the FreeBASIC Manual
'' from topic 'libffi'
'' Purpose: Call a typed C function through a libffi call interface.
'' Responsibilities: Match its C return type and provide ffi_arg result storage.
'' This example contains no closure allocation or persistent argument storage.
''
'' See Also: https://www.freebasic.net/wiki/wikka.php?wakka=ExtLibFfi
'' --------
#include "ffi.bi"
' Simple "puts" equivalent function
Function printer cdecl (ByVal s As ZString Ptr) As ULong
If s = 0 Then Return 0
Print *s
Return 42
End Function
' Initialize the argument info vectors
Dim s As ZString Ptr
Dim args(0 To 0) As ffi_type Ptr = {@ffi_type_pointer}
Dim values(0 To 0) As Any Ptr = {@s}
' Initialize the cif
Dim cif As ffi_cif
Dim result As ffi_status
result = ffi_prep_cif( _
@cif, _ ' call interface object
FFI_DEFAULT_ABI, _ ' binary interface type
1, _ ' number of arguments
@ffi_type_uint, _ ' return type
@args(0) _ ' arguments
)
' Call function
' libffi widens small integer results to ffi_arg when storing them.
Dim return_value As ffi_arg
If result = FFI_OK Then
s = @"Hello world"
ffi_call(@cif, FFI_FN(@printer), @return_value, @values(0))
' values holds a pointer to the function's arg, so to
' call puts() again all we need to do is change the
' value of s */
s = @"This is cool!"
ffi_call(@cif, FFI_FN(@printer), @return_value, @values(0))
Print Using "Function returned &"; return_value
Else
Print "Could not prepare the libffi call interface"
End 1
End If
'' end of helloworld.bas
Closures:
'' examples/manual/libraries/ffi/closures.bas
''
'' Example extracted from the FreeBASIC Manual
'' from topic 'libffi'
'' Purpose: Bind a C-style function pointer to a generic closure callback.
'' Responsibilities: Typed return storage, borrowed call data and cleanup.
'' This example contains no native trampoline implementation.
''
'' See Also: https://www.freebasic.net/wiki/wikka.php?wakka=ExtLibFfi
'' --------
#include "ffi.bi"
' Acts like puts with the file given at time of enclosure.
Sub Printer cdecl(ByVal cif As ffi_cif Ptr, ByVal ret As Any Ptr, ByVal args As Any Ptr Ptr, ByVal File As Any Ptr)
' libffi supplies ffi_arg storage even for a 32-bit integer return.
If ret = 0 Then Exit Sub
*CPtr(ffi_arg Ptr, ret) = 0
If args = 0 OrElse File = 0 OrElse args[0] = 0 Then Exit Sub
Dim s As ZString Ptr = *CPtr(ZString Ptr Ptr, args[0])
If s = 0 Then Exit Sub
Print #*CPtr(Integer Ptr, File), *s
*CPtr(ffi_arg Ptr, ret) = 42
End Sub
' Allocate the closure and function binding
Dim status As Integer = 1
Dim PrinterBinding As Function cdecl(ByVal s As ZString Ptr) As ULong
Dim closure As ffi_closure Ptr
closure = ffi_closure_alloc(SizeOf(ffi_closure), @PrinterBinding)
If closure <> 0 Then
' Initialize the argument info vector
Dim args(0 To 0) As ffi_type Ptr = {@ffi_type_pointer}
' Initialize the call interface
Dim cif As ffi_cif
Dim prep_result As ffi_status = ffi_prep_cif( _
@cif, _ ' call interface object
FFI_DEFAULT_ABI, _ ' binary interface type
1, _ ' number of arguments
@ffi_type_uint, _ ' return type
@args(0) _ ' arguments
)
If prep_result = FFI_OK Then
' Open console file to send to PrinterBinding as user data
Dim ConsoleFile As Integer = FreeFile()
'' CONS is the process console stream, not a disk path.
'' FB-LINTER: DISABLE-NEXT-LINE FBL-IO-005
Open Cons For Output As ConsoleFile
If Err = 0 Then
' Initialize the closure, setting user data to the console file
prep_result = ffi_prep_closure_loc( _
closure, _ ' closure object
@cif, _ ' call interface object
@Printer, _ ' actual closure function
@ConsoleFile, _ ' user data, our console file #
PrinterBinding _ ' pointer to binding
)
If prep_result = FFI_OK Then
' Call binding as a natural function call
Dim Result As ULong
Result = PrinterBinding("Hello World!")
Print Using "Returned &"; Result
If Result = 42 Then status = 0
End If
Close ConsoleFile
Else
Print "Could not open the console stream"
End If
End If
End If
' Clean up
If closure <> 0 Then
ffi_closure_free(closure)
End If
If status <> 0 Then Print "libffi closure example failed"
End status
' end of closures.bas