Skip to content

Bug: FTS extension fails to load on macOS arm64 from Rust static binary (missing GDSFunction symbol) #438

@byt3bl33d3r

Description

@byt3bl33d3r

Ladybug version

lbug Rust crate 0.16.0

What operating system are you using?

macOS 26.3.1, arm64 / Apple Silicon

ProductName:    macOS
ProductVersion: 26.3.1
BuildVersion:   25D2128
Darwin ... arm64

Rust toolchain:

rustc 1.95.0 (59807616e 2026-04-14)
host: aarch64-apple-darwin
LLVM version: 22.1.2

What happened?

When embedding LadybugDB through the Rust lbug crate on macOS arm64, INSTALL fts; succeeds but LOAD fts; fails when the host binary is built with the default/static lbug link mode.

The failure is an unresolved Ladybug C++ symbol from the downloaded FTS extension:

IO exception: Failed to load library: /Users/byt3bl33d3r/.lbdb/extension/0.16.0/osx_arm64/fts/libfts.lbug_extension
which is needed by extension: fts.
Error: dlopen(/Users/byt3bl33d3r/.lbdb/extension/0.16.0/osx_arm64/fts/libfts.lbug_extension, 0x0006): symbol not found in flat namespace '__ZN4lbug8function11GDSFunction15getPhysicalPlanEPNS_9processor10PlanMapperEPKNS_7planner15LogicalOperatorE'

Demangled symbol:

lbug::function::GDSFunction::getPhysicalPlan(lbug::processor::PlanMapper*, lbug::planner::LogicalOperator const*)

This happens even after deleting ~/.lbdb/extension/0.16.0/osx_arm64/fts and letting INSTALL fts; redownload the extension.

Are there known steps to reproduce?

In a Rust project using lbug = 0.16.0, create a database/connection and execute:

INSTALL fts;
LOAD fts;

In my downstream project this is currently triggered by a test that initializes the DB and loads FTS before creating an FTS index.

Relevant downstream query path:

conn.query("INSTALL fts;")?;
conn.query("LOAD fts;")?;

Linkage notes / investigation

The downloaded extension appears to rely on macOS dynamic lookup for Ladybug symbols:

$ otool -L ~/.lbdb/extension/0.16.0/osx_arm64/fts/libfts.lbug_extension
/Users/.../.lbdb/extension/0.16.0/osx_arm64/fts/libfts.lbug_extension:
    @rpath/libfts.lbug_extension
    /usr/lib/libc++.1.dylib
    /usr/lib/libSystem.B.dylib

No liblbug dependency is listed.

nm -m shows the failing symbol is an unresolved dynamically looked up symbol in the extension:

(undefined) external __ZN4lbug8function11GDSFunction15getPhysicalPlanEPNS_9processor10PlanMapperEPKNS_7planner15LogicalOperatorE (dynamically looked up)

The LadybugDB/extensions build files seem consistent with this: dynamic extensions on Apple are linked with -undefined dynamic_lookup.

When the Rust binary is built in the default/static mode, the final executable does not appear to export that GDSFunction::getPhysicalPlan symbol, so dlopen cannot resolve it.

Additional experiment

Building the Rust project with LBUG_SHARED=1 changes the failure mode: the FTS extension no longer fails with the missing GDSFunction::getPhysicalPlan symbol, which supports the theory that this is a host symbol visibility/linkage problem for the static Rust embedding on macOS.

However, with LBUG_SHARED=1, my downstream test then hits a different Ladybug assertion during FTS/indexing:

Assertion failed: (hashIndexStorageInfo.overflowHeaderPage == INVALID_PAGE_IDX), function load, file src/storage/index/hash_index.cpp, line 483.

Also, the shared build currently produces a binary linked to @rpath/liblbug.0.dylib; direct execution fails unless DYLD_LIBRARY_PATH points at the built Ladybug dylib, because no usable rpath is embedded by the Rust crate build.

Expected behavior

INSTALL fts; LOAD fts; should work from a Rust lbug embedding on macOS arm64, or the Rust crate should document/encode the required link mode and runtime library path for dynamically loaded extensions.

Related issues

This looks conceptually related to extension ABI/loading issues like:

But I could not find an existing issue for the specific macOS arm64 + Rust static embedding + GDSFunction::getPhysicalPlan dynamic lookup failure.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions