std::dylib
std::dylib is the opt-in standard-library facade for loading dynamic
libraries and resolving symbols at runtime.
Use it when the library or symbol is chosen at runtime. Use ext declarations
instead when the foreign symbol is a fixed build-time dependency and normal
linker diagnostics are the right failure mode.
The shipped hosted backend is based on the platform dynamic loader:
- POSIX/macOS:
dlopen(3),dlsym(3),dlclose(3), anddlerror(3). - Unsupported targets return ordinary
Resultfailures withErrorKind::Unsupported. - On
linux/x86_64, importingstd::dylibautomatically adds the libc component that providesdlopen(libdl.so.2on glibc,libc.soon musl) for executable/shared-library outputs. macOS resolves the dynamic-loader APIs throughlibSystem.
Exported API#
is_supported() -> bool: reports whether the bundled runtime has a hosted dynamic-loader backend for the current target.ErrorKind: stable loader failure categories:InvalidInput,OpenFailed,SymbolNotFound,CloseFailed,Unsupported, andUnknown.Failed { code, message }: loader failure payload.kind()converts the stable code toErrorKind.messageis a borrowed diagnostic string when the platform loader provides one, otherwise a stdlib fallback string.OpenFlags { bits }: loader flags withlazy(),now(),local(),global(),defaults(), andor(...).Library { handle }: owning dynamic-library handle wrapper.Symbol { addr }: non-owning raw symbol address wrapper.LibraryResult = Result(Library, Failed).SymbolResult = Result(Symbol, Failed).
Minimal Workflow#
- Check
is_supported()when the program should also run on targets without a hosted dynamic loader. - Open a handle with
open(path),open_with_flags(path, flags), oropen_self(). - Resolve a symbol with
Library.symbol(name). - Convert
Symbol.address()to the exactc_fntype withas raw. - Call the function pointer.
- Let scope cleanup close the
Library, or callclose()when the program must observe a close failure before continuing.
import { ErrorKind, Library, LibraryResult, OpenFlags, Symbol, SymbolResult, is_supported, open, open_self, open_with_flags } from "std/dylib";
type StrLen = c_fn (u64) -> u64;
fn main () -> int {
if !is_supported() {
return 0;
}
let lib_opt: Library? = open_self().unwrap();
if lib_opt == None {
return 1;
}
let lib: Library = lib_opt ?? Library.invalid();
let sym_opt: Symbol? = lib.symbol("strlen").unwrap();
if sym_opt == None {
return 1;
}
let sym: Symbol = sym_opt ?? Symbol.invalid();
let strlen: StrLen = sym.address() as raw StrLen;
let n: u64 = strlen("hello" as raw u64);
assert(n == 5, "strlen should count the hello string bytes");
return 0;
}
See examples/std_dylib_strlen.slk for the runnable version.
Opening Libraries#
Use open(path) for default eager/local loader behavior:
let lib_r: LibraryResult = open("/usr/lib/libSystem.B.dylib");
Use open_with_flags(path, flags) when loader policy matters:
let flags: OpenFlags = OpenFlags.now().or(OpenFlags.global());
let lib_r = open_with_flags("/usr/lib/libSystem.B.dylib", flags);
Use open_self() to resolve symbols from the current process image and the
process-global loader namespace. This is useful for examples, plugin systems
that intentionally export host symbols, and tests that need a stable symbol
without depending on a platform-specific library path.
Function Pointer Typing#
Symbol.address() returns a raw u64 address. It is intentionally not typed.
The caller must assert the ABI by casting to a c_fn alias:
type StrLen = c_fn (u64) -> u64;
let strlen: StrLen = sym.address() as raw StrLen;
The declared c_fn type must match the foreign symbol exactly:
- parameter count,
- parameter widths and signedness,
- return type,
- platform calling convention,
- pointer ownership and lifetime rules.
Silk cannot validate dynamic symbol signatures at runtime. A mismatched signature is undefined behavior in the same way it is undefined behavior in C.
Silk strings can be passed to C string parameters by converting the string view to its byte pointer:
let ptr: u64 = "hello" as raw u64;
The compiler emits string literal backing bytes with a trailing NUL terminator; the Silk string length excludes that terminator.
Errors#
Prefer matching on LibraryResult and SymbolResult, then inspect
Failed.kind() for stable control flow:
match (lib.symbol("required_symbol")) {
SymbolResult::Ok(sym) => {
let _ = sym.address();
},
SymbolResult::Err(failure) => {
assert(
failure.kind() == ErrorKind::SymbolNotFound,
"required_symbol should be missing in this example"
);
},
};
Failed.message is for diagnostics. It may be a platform loader string or a
stdlib fallback string; do not parse it for program logic.
See examples/std_dylib_errors.slk for a runnable failure-path example.
Ownership#
Library owns the platform loader handle:
Library.close()closes the handle and invalidates it on success.- Calling
close()on an invalid handle is a no-op success. - Scope cleanup calls
Library.drop(), which delegates toclose()and ignores close failures. Symboldoes not own anything. It is valid only while the owningLibraryremains open.
Do not cache a Symbol address past Library.close() unless the platform
library documentation explicitly guarantees the symbol remains valid.
Testing and Distribution#
For portable tests, prefer open_self() and a stable process symbol such as
strlen. For application integration tests, use open(path) with a
platform-specific test fixture and keep the expected path in the test
environment or package manifest.
When distributing applications:
- On Linux, ensure the target system has the library you open and that the path
or loader search path is correct.
std::dylibadds the libc component needed for the dynamic-loader API itself; it does not add the library you choose at runtime. - On macOS, use absolute framework/library paths when the symbol provider is a system framework, or package private libraries beside the app and open the path your app controls.
- Prefer
OpenFlags.defaults()unless the foreign library explicitly requires lazy binding or global symbol visibility.
Related Guides#
Implementation#
std::dylib delegates to std::runtime::dylib, which delegates to
std::runtime::posix::dylib in the shipped hosted stdlib. The C runtime shim is
implemented in src/silk_rt_dylib.c and is statically linked as part of the
bundled runtime support when reachable.
Source repository · Edit this page · View Markdown