Peta::FFI#

📦 std

Call C library functions from Perl without writing XS or a 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. A fourth helper, scan, enumerates the shared libraries the dynamic linker already knows about, which is useful for finding the right soname before you open it.

Reach for this module when:

  • The function you need lives in a system library (libm, libc, libuuid, libcrypto) and writing a full XS binding is more ceremony than the problem deserves.

  • You already know the C prototype and want a one-liner, not a build step.

  • You are prototyping against a third-party .so and will decide later whether to graduate to a proper binding.

It is not a replacement for XS when you need callbacks from C back into Perl, struct layouts beyond scalars, or automatic header parsing. For those, write an XS module.

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.

Signature strings#

Every call takes a signature string of the form (args)ret, where each position is one single-character type code:

Code

C type

Perl side

v

void

return only; 1 is returned

i

int

integer

l

long

integer

L

unsigned long / size_t

integer

d

double

float

f

float

float

p

const char *

input string

P

mutable buffer

output buffer, auto-allocated

o

opaque pointer

raw address as integer

A signature with no arguments is written ()ret, e.g. ()i for an int-returning no-arg function. P allocates a zeroed buffer big enough for the passed scalar and hands the pointer to the callee - typical use is for functions like uuid_generate that write into a caller-supplied buffer.

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. 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 directly as a p string when the callee only reads it during the call.

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

Modules#

  • Peta::FFI::Cairo - Native reimplementation of the Perl Cairo module against libcairo.so.2.

  • Peta::FFI::Libc - Call common libc functions from Perl without writing a single line of FFI glue.

  • Peta::FFI::UUID - Generate and parse RFC 4122 UUIDs via the system’s libuuid library.

Functions#

Library loading#

ffi_dlopen#

Open a shared library and return a handle suitable for passing to call.

ffi_dlclose#

Release a library handle previously returned by dlopen.

Function calls#

ffi_call#

Invoke a C function from an opened library and return its result.

Symbol introspection#

ffi_scan#

List the shared libraries the dynamic linker already knows about.

Other Functions#

ffi_peek#

Read $len raw bytes at address $ptr into a Perl byte string.

ffi_peek_cstr#

Read the NUL-terminated C string at address $ptr.

ffi_poke#

Copy a Perl byte string into memory at address $ptr.

ffi_alloc#

Allocate $len zeroed bytes of C memory; returns the address.

ffi_free#

Release memory obtained from alloc.

ffi_pack_ptr#

Pack an address into a native pointer-sized byte string.

Synopsis

**Build a char*[] for PQexecParams-style APIs:**

my $vec = join "", map { Peta::FFI::pack_ptr($_) } @addrs;

What you get back

The address as native-endian bytes, sized to the platform pointer width - the in-memory representation a C pointer slot holds.

ffi_unpack_ptr#

Unpack a native pointer-sized byte string into an address.