Perl API on Linux: Find Stable XS Interfaces Before Coding
This guide shows how to use perlapi(1) as a working reference when writing or reviewing XS and embedded-Perl C code. You will identify the installed Perl build, find the matching core headers, check an API entry's contract, and reject symbols that are undocumented or experimental.
The route
Jump straight to the step you need, or tick off Done means at the end.
Prerequisites: a shell, the perl and perldoc commands, and the perl-doc package. Allow about 15 minutes for a first pass. You do not need root access for any step.
1. Confirm the Perl build you are targeting
API headers and binary interfaces belong to a particular Perl build. Start by recording the version, architecture and shared-library setting on the machine where the extension will be built.
perl -v
perl -V:version -V:archname -V:useshrplib
On this machine the result identifies Perl 5.38.2, x86_64-linux-gnu-thread-multi, with shared libraries enabled:
This is perl 5, version 38, subversion 2 (v5.38.2)
version='5.38.2';
archname='x86_64-linux-gnu-thread-multi';
useshrplib='true';
Checkpoint
Keep this output with the build notes. If a production host reports a different Perl version or thread configuration, repeat the checks there rather than assuming the same API details.
2. Open the installed API reference
perlapi is generated documentation for functions, macros, flags and variables intended for extension writers. It is a catalogue, not a tutorial, and it cross-refers to documents such as perlguts, perlxs and perlembed.
perldoc -l perlapi
man perlapi
The first command prints the local source path:
/usr/share/perl/5.38/pod/perlapi.pod
Use the pager's search function on the section names, rather than reading the alphabetical list from the beginning. Useful starting points are AV Handling, Embedding, Threads, and Interpreter Cloning, Exception Handling (simple) Macros, Stack Manipulation Macros, SV Handling and XS.
If you need a plain text copy for a review or a narrow search, keep the compressed manpage read-only and stream it:
zcat /usr/share/man/man1/perlapi.1.gz | col -b | less
Do not edit the generated file. Package upgrades can replace it, and a local edit would no longer describe the installed interpreter.
3. Match an API entry to the headers
The API page gives a purpose and, usually, a C declaration. For example, the array functions describe AV values. av_clear(AV *av) removes all elements but leaves the array allocated, while av_undef(AV *av) also frees the memory used to store the array. Both can trigger destruction of contained values.
Find the core include directory from Perl itself, then inspect the actual headers selected by this build:
perl -MConfig -e 'print "$Config{archlib}/CORE\n"'
perl -MConfig -e 'print "$Config{archlib}/CORE\n"' | while read core; do
ls -l "$core/perl.h" "$core/embed.h" "$core/EXTERN.h"
done
The installed headers are under /usr/lib/x86_64-linux-gnu/perl/5.38/CORE on this host. Look up a declaration or macro by its exact spelling:
core=$(perl -MConfig -e 'print "$Config{archlib}/CORE"')
rg -n 'av_clear|av_undef|pTHX|PL_' "$core/perl.h" "$core/embed.h" "$core/EXTERN.h"
The perlapi entry is the contract you read first; the header confirms the spelling and build-specific expansion. Do not infer that an unlisted macro or global is public. The document says Perl API globals use the PL_ prefix, and warns that other globals and macros may change or disappear.
4. Check context, ownership and failure behaviour
Three details cause many otherwise plausible XS changes to fail.
- Interpreter context: threaded builds use context parameters such as
pTHXandaTHX_. Some entries have a_nocontextform for code that does not already have that context. Follow the declaration shown for the exact function. - Ownership: entries describe reference-count transfers and whether a returned
SV *is borrowed or owned. A function such asav_poptransfers one reference to its caller. Pair that knowledge with the surroundingperlgutsguidance before adding cleanup. - Exceptions:
croak,croak_sv,dieand the warning functions do not behave like ordinary C returns. The page distinguishes context-aware and no-context forms, and notes that some calls never return normally.
For example, the API reference says that croak must be called as Perl_croak with an aTHX_ parameter in the relevant source form. Copying a short signature from memory is not a safe substitute for checking the entry and the surrounding XS conventions.
5. Reject unstable entries before they reach production
Scroll to the end of perlapi before choosing a function. The Undocumented elements section lists API-flagged functions whose interfaces are subject to change. The following section marks experimental elements as riskier still. In the installed 5.38.2 documentation, examples include newXS_flags, hv_store_flags and thread locale helpers.
There is also a deprecated undocumented list. The local page says there are currently no entries in that list, but that is a fact about this installed documentation, not a promise for another Perl release.
Warning
Do not use an undocumented or experimental symbol merely because it appears in a header or links successfully. Prefer a documented API entry, or isolate the dependency and record the exact Perl versions you support. If no documented interface meets the need, consult the related Perl documentation or the Perl development community before shipping it.
6. Verify the review result
Before compiling, run the small audit again and save the outputs beside the extension's build notes:
perl -V:version -V:archname -V:useshrplib
perldoc -l perlapi
man -w perlapi
perl -MConfig -e 'print "$Config{archlib}/CORE\n"'
man -w should report the installed manual path, while perldoc -l reports the Perl POD path. A mismatch between those paths is a prompt to check package installation and PATH, not a reason to mix documentation from another Perl.
Done means
- You recorded the target Perl version, architecture and thread build.
- You opened the local
perlapi(1)and followed its cross-references where needed. - You matched declarations to the target's
COREheaders. - You checked context parameters, ownership and non-returning error paths.
- You excluded undocumented, experimental and deprecated interfaces unless a deliberate compatibility decision supports them.