Peta::FFI#
Call C library functions from Perl with no build step and no binding crate.
Peta::FFI is the dynamic FFI layer pperl ships for reaching into shared libraries at runtime. You open a library with dlopen, call any of its exported functions through call by giving a type signature string, and release the handle with dlclose. scan finds the libraries in a list of directories, which is useful for finding the right file before you open it.
This is how Perl code here reaches a C library: pperl loads no compiled extension modules, so anything living in a .so is called either through this module or through one of the bindings built on it. pperl’s FFI::Platypus is one of them: its calls, types, records and closures are this module’s, so whatever FFI::Platypus can express, Peta::FFI can, in its own signature language.
Reach for it when:
The function you need lives in a system library (
libm,libc,libuuid,libcrypto).You already know the C prototype and want a one-liner, not a build step.
You are calling into a third-party
.sothat no pre-baked wrapper covers.
C calling back into Perl is supported: closure wraps a coderef as a function pointer you can hand to any C API that wants one (see the Callbacks category). C structs are records: record declares one as a Perl class, and a signature passes and returns it by value or by pointer (see the Records category). A library’s global variables are reached with symbol, fetch/store and variable.
What it does not do is derive types from a header: you read the C prototype and write the matching signature by hand, and for a struct its members in order, since a signature that disagrees with the real prototype is undefined behaviour rather than an error.
The two sibling modules Peta::FFI::Libc and Peta::FFI::UUID are pre-baked convenience wrappers built on top of this layer - they handle the dlopen / call / dlclose dance for you for a fixed set of functions. If your need is covered by one of them, prefer it over hand-rolled FFI.
A call that can block for good - statfs on a dead NFS mount - runs on a Peta::FFI::Pool thread instead, with a deadline, and reports how it went as an event. A library that calls back on threads of its own gets a queued closure, whose calls wait in a Peta::FFI::Queue for Perl’s event loop.
Signature strings#
Every call, function and closure takes a signature string of the form (args)ret, where each position is one type:
Type | C type | Perl side |
|---|---|---|
|
| return only; |
|
| integer |
|
| integer |
|
| unsigned integer |
|
| integer |
|
| unsigned integer |
|
| perl’s true or false |
|
| float |
|
| float |
|
| float (rounded to a double) |
|
|
|
|
| input string; a returned string is copied |
|
| a result, copied, then freed; |
| mutable buffer | output buffer; |
| opaque pointer | raw address as integer |
| a C object’s pointer | an object of a declared handle type |
|
| an object of a declared record class |
|
| the same object, by pointer |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| the same, copied, never freed |
|
|
|
result |
| an array reference of copies |
result |
| a copy of the bytes |
A signature with no arguments is written ()ret, e.g. ()i for an int-returning no-arg function. Types may be separated by blanks: (u64 <My::Pool>)b. A variadic function lists its named parameters, a ;, and the types this call passes through its ...: (iL;o)i is ioctl(int, unsigned long, ...) passing one pointer.
An unsigned type reads its argument as an unsigned integer, so 2**64-1 passes as itself, not as -1, and returns above 2**63 come back unsigned. s is for C functions whose returned string the caller owns; function names the free function that releases it. <Class> is a typed handle declared with handle_type, {Class} a record declared with record. An argument is read once, as an XS function reads it, so a regex capture or a tied scalar passes its value.
*T and T[N] take the numbers, o, p (an array of C strings, char **) and b: (d*i)d is frexp(double, int *), called with \my $exp, and (i32[]LLo)v is qsort sorting \@numbers in place. T[N] also takes handles and records, and C’s changes to the records are written back: ({My::Point}[]LLo)v sorts \@points. T[0] is an array of o, p or handles that C reads up to a NULL the call appends, as execv’s argv or a NULL-terminated list of handles.
Some C functions hand over what they allocate through a pointer the caller passes. *<Class> is a handle stored there, as sqlite3_open stores its sqlite3 *: called with \my $db, $db becomes the object, owned by the caller and destroyed with it; a handle already in $db goes in, and stays the same object if C leaves it. *s is a string stored there, copied into the scalar and freed.
*T[r], *T[#k] and *T[0] are an array of T the callee allocates and stores there: as long as the function’s integer result ([r]), as argument k ([#k], an integer or an * integer, counted from 1), or up to a NULL element ([0], for o, p, s and handles). (<My::Conn>*<My::Item>[r]u32)i lists a connection’s items: called with \my $items, $items becomes an array reference of objects, each the caller’s; a reference to an array is filled instead. Strings (*s[...]) are copied and each freed, numbers and o read, records copied. A negative length is the callee’s error and leaves undef. The array itself is freed with function’s free - or, for records, with its release, (array, count), the library’s function that also frees what the records point to. Records keep the array until the last of them is gone, so a record’s p member reads its string for as long as the record lives; a bare copy of its bytes does not keep it.
*P[r] and *P[#k] are bytes the callee allocates and stores there, with the length as for an array: one byte string, NULs included, where *u8[#k] would give an array of numbers. librados’s rados_mon_command hands over its reply this way, as a char ** and a size_t * freed with rados_buffer_free: (op[]LPL*P[#7]*L*P[#9]*L)i with free => "rados_buffer_free", the input buffer passed as \$inbuf and the reply taken with \my $out, \my $outlen, \my $status, \my $statuslen. A NULL leaves undef, and so does a negative length; the buffer is freed with function’s free. A buffer ended by a NUL is an *s. Bytes the callee only lends are &P[r] and &P[#k], and &P[#k] is also a result: copied, never freed, as the lent arrays below.
&T[r], &T[#k] and &T[0] are the same for an array the callee only lends - a pointer into memory it keeps, as libxkbcommon’s xkb_keymap_key_get_syms_by_level hands out a keymap’s keysyms: (<Keymap>u32u32u32&u32[r])i. The elements are copied and the array is never freed. They are numbers, o, p, b or records; an s, which the caller frees, and a handle, whose object owns its pointer, cannot be lent.
&T[#k] and &T[0] are also a result: the array C returns, as libxkbcommon’s xkb_compose_table_entry_sequence returns an entry’s keysyms and stores their count through a size_t *: (o*L)&u32[#2], called as $f->($entry, \my $n). The call returns an array reference of the copies, or undef for a negative length or a NULL with elements. The length is read after the call, from an integer or an * integer argument; [r] is no form here, since the result is the array itself.
P passes the bytes of the scalar it references, which hold what the callee wrote afterwards: my $buf = "\0" x 64; call(..., \$buf). Given a string or a number instead, it passes a zeroed buffer of that length or that many bytes (at least 16) that nobody reads back.
o is the handle type for C APIs built on opaque structs (PGconn*, sqlite3*, MYSQL*): the raw address travels as a Perl integer, is never dereferenced by the runtime, and undef/0 passes NULL. Never use p for such pointers - p returns copy-until-NUL string semantics, which is wrong and unsafe for non-string data.
Memory primitives#
For C APIs that traffic in caller-owned memory (out-parameters, pointer arrays, length-counted buffers) the module provides the pointer toolbox: alloc/free, peek/peek_cstr, poke, and pack_ptr/unpack_ptr for bytes, fetch/store for a value of any type at an address, and sizeof/alignof. Addresses are ordinary Perl integers as with o. A pointer array (e.g. char *argv[]) is built portably as join "", map pack_ptr($_), @addrs poked into an alloced block, or passed as a p[] array.
Synopsis#
use Peta::FFI;
my $libm = Peta::FFI::dlopen("libm.so.6");
my $root = Peta::FFI::call($libm, "sqrt", "(d)d", 2.0);
Peta::FFI::dlclose($libm);
print $root, "\n"; # 1.4142135623731
Peta::FFI::record("Div", quot => "i", rem => "i"); # div_t
my $d = Peta::FFI::call(undef, "div", "(ii){Div}", 17, 5);
print $d->quot, " r ", $d->rem, "\n"; # 3 r 2
Modules#
Peta::FFI::EventSource- events from a C library’s own threads, drained on Perl’sPeta::FFI::LMDB- Lightning memory-mapped databases: transactional key-value stores in a file, over the system’s liblmdb.Peta::FFI::Libc- Call common libc functions from Perl without writing a single line of FFI glue.Peta::FFI::PCRE2- Compile and run PCRE2 regular expressions via the system’s libpcre2-8 library.Peta::FFI::Pool- Run C functions on worker threads, so that a call which hangs cannot hang the program.Peta::FFI::Queue- Callbacks a C library makes from threads of its own, queued for Perl’s event loop.Peta::FFI::UUID- Generate and parse RFC 4122 UUIDs via the system’s libuuid library.
Functions#
Library loading#
dlopen#
Open a shared library and return a handle suitable for passing to call.
dlclose#
Release a library handle previously returned by dlopen.
Function calls#
call#
Invoke a C function from an opened library and return its result.
function#
Prepare a C function once and return it as a code reference.
attach#
Prepare a C function once and install it as a named Perl sub.
errno#
errno as the last C call through Peta::FFI left it.
Symbol introspection#
symbol#
The address of a symbol - a function or a variable - in a library.
variable#
A library’s global variable as a Perl scalar.
scan#
Find the shared libraries in a list of directories.
Handles#
handle_type#
Declare a Perl class for a C object type, with its destructor.
release#
Destroy a handle’s C object now, ahead of its last reference.
Records#
record#
Declare a record class: a C struct, passed by value or by pointer.
union#
Declare a union class: a C union, reached by pointer and in memory.
sizeof#
The size of a type in bytes, as C’s sizeof.
alignof#
The alignment of a type in bytes, as C’s _Alignof.
Callbacks#
closure#
Wrap a Perl coderef as a C-callable function pointer.
closure_free#
Release a closure created by closure.
Memory#
fetch#
Read the value of a type at an address.
store#
Write a value of a type at an address.
peek#
Read $len raw bytes at address $ptr into a Perl byte string.
peek_cstr#
Read the NUL-terminated C string at address $ptr.
poke#
Copy a Perl byte string into memory at address $ptr.
alloc#
Allocate $len zeroed bytes of C memory; returns the address.
free#
Release memory obtained from alloc.
pack_ptr#
Pack an address into a native pointer-sized byte string.
unpack_ptr#
Unpack a native pointer-sized byte string into an address.