Linking & FFI

Extern functions (calling native code)

tape
@link("kernel32.dll");
extern fn GetTickCount() -> u32;
extern fn Sleep(ms: u32);

extern fn declares a function that lives in a native library. The compiler generates a calling convention trampoline automatically.

Custom symbol names

If the native symbol differs from the tape function name:

tape
@link("user32.dll");
extern("MessageBoxA") fn message_box(hwnd: *u8, text: *u8, caption: *u8, flags: u32) -> i32;

The extern("symbol") form specifies the exact linker symbol to resolve.

Declares which native library to link against:

tape
@link("user32.dll");         // Windows DLL
@link("opengl32.dll");       // Windows DLL
@link("libSystem.B.dylib");  // macOS system library
@link("libc.so.6");          // Linux shared library

All extern fn declarations following a @link are resolved from that library. Multiple @link directives can appear in one file.

Exported functions (callable from native code)

tape
@export
pub fn tape_init(config: *u8) -> i32 {
    return 0;
}

@export marks a function for inclusion in the output’s export table (DLL exports on Windows, .sto vtable on Uusi).

Custom export symbol:

tape
@export("my_custom_name")
pub fn internal_name() -> i32 {
    return 42;
}

The compiler verifies at build time that @link libraries exist and all extern symbols resolve (on the host platform). Skip with --skip-link-check for cross-compilation.

Cross-module linking

Tape modules link to each other automatically through the import system. The compiler:

  1. Parses all imported modules recursively
  2. Injects extern stubs for all pub declarations with qualified names (module.fn_name)
  3. Resolves cross-module calls at link time

Internal symbol naming uses dot-separated qualified names (io.println, str.len) and double-underscore for methods (Button__paint, Vec2__op_add).

Output formats

FlagOutputDescription
(default).exe / ELF / Mach-OStandalone executable
--dll.dll (PE64)Windows DLL with export table
--sto.sto (ELF64)Uusi shared object with vtable exports

DLL output

bash
tape build lib.tape --dll -o mylib.dll

Only functions marked @export appear in the DLL’s export table.

Uusi shared object

bash
tape build lib.tape --sto --target uusi -o mylib.sto

On Uusi, @link externs are resolved at runtime via syscall 23 (library load). The compiler emits thunks that:

  1. Load the .sto library
  2. Read the vtable pointer from the library handle
  3. Jump to the function pointer at the correct vtable slot

Platform-specific linking

Platform@link resolves toMechanism
WindowsDLL import (IAT)PE64 import table
Linux.so symbolELF64 dynamic linking
macOS.dylib symbolMach-O dynamic linking
Uusi.sto vtable slotThunk + syscall 23
PlannedLinux .so and macOS .dylib shared library is not yet implemented. Currently only Windows --dll and Uusi --sto produce shared libraries with exported symbols. Planned flags: --so (Linux ELF64 shared object) and --dylib (macOS Mach-O dynamic library). @link from .so and .dylib already work on those platforms.

Last modified: