External Libraries and FFI

seed interoperates with platform libraries through explicit extern declarations and linker inputs. Foreign calls are unsafe until an audited seed wrapper establishes their type, ownership, lifetime, error, and synchronization contracts.

Foreign declarations

The clean syntax is:

extern "c" fn c_sqrt(value: f64) -> f64 link_name "sqrt";

pub fn sqrt_value(value: f64) -> f64 {
    unsafe { c_sqrt(value) }
}

The optional string after extern selects the calling convention. link_name selects the foreign symbol when it differs from the seed declaration name. Declarations may end with a semicolon.

An extern declaration does not identify or load a shared library by itself. Direct compiler builds provide native inputs explicitly:

seed/compiler/llvm/build/seed \
  --emit-binary app \
  --link-library /usr/lib/x86_64-linux-gnu/libm.so.6 \
  app.sd

The system-library path is host-specific and should be discovered by the package/install procedure rather than hard-coded as a portable value. --link-library accepts a path to a shared object/archive or the name of an installed seed package at target levels that support linking. --link-object adds an object file. The repository launcher forwards supported direct compiler flags when an application needs explicit native inputs.

Package requirements

A package that contains extern declarations must:

Example manifest fragment:

[package]
name = "math-wrapper"
version = "0.1.0"
kind = "lib"
platforms = ["linux-x86-64", "linux-aarch64"]

The platform declaration is validation and compatibility metadata. It does not install the operating-system dependency.

Safe-wrapper checklist

Expose an ordinary safe pub fn only when its implementation enforces every foreign precondition relevant to callers:

If the wrapper cannot establish these conditions, expose it as unsafe fn and document the caller obligations.

Pointers and handles

Raw pointers use explicit pointee mutability:

extern "c" fn foreign_open() -> *mut u8;
extern "c" fn foreign_close(handle: *mut u8);

Checking a raw value does not create safe provenance. Prefer an audited nominal owner that stores the handle, rejects null during construction, calls the foreign release function from its destructor, and exposes only operations whose preconditions it can maintain.

Do not represent a foreign owned handle as a copyable integer merely for convenience. That loses exact cleanup and permits duplicate release.

Borrowed foreign views

An ordinary foreign pointer or slice return has no safe lifetime proof. @static_view is available only on an extern whose ABI guarantees that the returned str or slice has process/static lifetime:

@static_view extern "c" fn foreign_version() -> str;

This attribute is a trusted provenance assertion, not a lifetime extension, and the call remains unsafe. Input-derived or handle-derived foreign views should be kept behind a wrapper owner rather than mislabeled static.

Resources across seed libraries

Native seed libraries use a profile-qualified runtime/allocator identity. Passing string, bytes, shared_bytes, or destructor-owned values across a seed library boundary is supported only when the producer and consumer artifacts have compatible target, ABI, build profile, runtime profile, runtime features, and interface identity.

An arbitrary C library does not understand those owned layouts. Prefer scalar, plain C-layout struct, pointer, and pointer/length ABIs at third-party boundaries, with conversion and ownership retained inside the seed wrapper.

Target limitations

Object emission is not proof of FFI or hosted linking. Consult seed/compiler/llvm/tests/GATE14_TARGET_MATRIX.tsv before documenting an external package as supported on a target. In particular, the current wasm32-wasi row is object-only and does not provide an operational WASI FFI or runtime.

Grove compatibility note

Many wrappers under the top-level grove/ tree use superseded foreign syntax, capitalized types, implicit runtime helpers, or manual ownership conventions. Their presence is not evidence that they compile with the operational LLVM language. Port and verify a wrapper before listing it as a current external library.