libffi
 
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