Skip to content

Link duckdb_generated_extension_loader when linking duckdb statically - #63

Merged
adsharma merged 1 commit into
mainfrom
fix/duckdb-static-extension-loader
Aug 22, 2026
Merged

Link duckdb_generated_extension_loader when linking duckdb statically#63
adsharma merged 1 commit into
mainfrom
fix/duckdb-static-extension-loader

Conversation

@adsharma

Copy link
Copy Markdown
Contributor

Problem

Building with GEN=Ninja make shell EXTENSION_STATIC_LINK_LIST='httpfs' (which flips all extensions to static linking) failed at the final link with undefined symbols:

duckdb::DuckDB::LoadStaticExtension<duckdb::ParquetExtension>()
duckdb::DuckDB::LoadStaticExtension<duckdb::JsonExtension>()
...

Root cause

When DuckDB_USE_STATIC_LIBS=ON, find_package(DuckDB) resolves DuckDB_LIBRARIES to just duckdb_static. But a statically-linked duckdb requires its companion libduckdb_generated_extension_loader.a archive (which provides DuckDB::LoadStaticExtension<T> hooks for duckdb's own bundled extensions), and the loader in turn references the bundled extension archives (json, parquet, icu, autocomplete, core_functions), all exported by the DuckDB CMake package.

Fix

When DuckDB_USE_STATIC_LIBS is set, link duckdb_generated_extension_loader after ${DuckDB_LIBRARIES}, followed by every ${ext}_extension target listed in DuckDB_EXTENSIONS. Order matters: the loader references symbols from both duckdb_static and the extension archives.

Verified with GEN=Ninja make shell EXTENSION_STATIC_LINK_LIST='httpfs' against Homebrew duckdb 1.5.1.

Homebrew's libduckdb_static.a requires the companion
libduckdb_generated_extension_loader.a archive, which provides the init
hooks for duckdb's own statically-bundled extensions (json, parquet,
icu, autocomplete, core_functions). Without it, statically linking the
duckdb extension fails with undefined symbols like
duckdb::DuckDB::LoadStaticExtension<duckdb::ParquetExtension>().

Link the exported duckdb_generated_extension_loader target after
${DuckDB_LIBRARIES} whenever DuckDB_USE_STATIC_LIBS is set, followed by
each bundled extension archive listed in DuckDB_EXTENSIONS (order
matters: the loader references symbols from both).
@adsharma
adsharma merged commit 68ecf78 into main Aug 22, 2026
2 checks passed
@adsharma
adsharma deleted the fix/duckdb-static-extension-loader branch August 22, 2026 16:48
adsharma added a commit to LadybugDB/ladybug that referenced this pull request Aug 22, 2026
Both ladybug's third_party/mbedtls and a statically-linked duckdb
(libduckdb_static.a) bundle mbedtls with plain, unprefixed mbedtls_*
symbols. When both end up on one link line (e.g. GEN=Ninja make shell
EXTENSION_STATIC_LINK_LIST='httpfs'), the linker reports ~19 duplicate
symbol errors for _mbedtls_cipher_*.

Fix by wrapping our vendored copy in namespace lbug_mbedtls:

- third_party/ports/mbedtls/do-patch.py (new): port patch script,
  following the zstd port pattern. It renames .c -> .cpp, strips the
  now-dead extern "C" guards (the copy is compiled as C++ everywhere),
  and wraps top-level code segments in balanced namespace pairs.
  Segmentation tracks preprocessor conditional context and brace depth
  per line, so sibling conditional branches (MBEDTLS_AES_ALT vs
  MBEDTLS_SELF_TEST) and mid-declaration conditionals (lone 'static'
  followed by a conditional attribute block) stay balanced. It also
  absorbs the previously-missing format_c_to_cpp.py step, making the
  port runnable again.
- ports/mbedtls/Makefile: invoke do-patch.py during build.
- third_party/mbedtls: vendored sources regenerated via the script
  (57 files; only namespace pairs and extern "C" removals).

Public headers re-expose names via 'using namespace lbug_mbedtls', so
in-tree consumers (src/common/sha256.cpp,
extension/httpfs/src/crypto.cpp) are unchanged.

Requires LadybugDB/extensions#63 (submodule bump included here).
Verified with GEN=Ninja make shell EXTENSION_STATIC_LINK_LIST='httpfs':
the link succeeds, libmbedtls.a exports zero plain _mbedtls_* symbols,
and the shell passes a smoke query.
adsharma added a commit to LadybugDB/ladybug that referenced this pull request Aug 22, 2026
Both ladybug's third_party/mbedtls and a statically-linked duckdb
(libduckdb_static.a) bundle mbedtls with plain, unprefixed mbedtls_*
symbols. When both end up on one link line (e.g. GEN=Ninja make shell
EXTENSION_STATIC_LINK_LIST='httpfs'), the linker reports ~19 duplicate
symbol errors for _mbedtls_cipher_*.

Fix by wrapping our vendored copy in namespace lbug_mbedtls:

- third_party/ports/mbedtls/do-patch.py (new): port patch script,
  following the zstd port pattern. It renames .c -> .cpp, strips the
  now-dead extern "C" guards (the copy is compiled as C++ everywhere),
  and wraps top-level code segments in balanced namespace pairs.
  Segmentation tracks preprocessor conditional context and brace depth
  per line, so sibling conditional branches (MBEDTLS_AES_ALT vs
  MBEDTLS_SELF_TEST) and mid-declaration conditionals (lone 'static'
  followed by a conditional attribute block) stay balanced. It also
  absorbs the previously-missing format_c_to_cpp.py step, making the
  port runnable again.
- ports/mbedtls/Makefile: invoke do-patch.py during build.
- third_party/mbedtls: vendored sources regenerated via the script
  (57 files; only namespace pairs and extern "C" removals).

Public headers re-expose names via 'using namespace lbug_mbedtls', so
in-tree consumers (src/common/sha256.cpp,
extension/httpfs/src/crypto.cpp) are unchanged.

Requires LadybugDB/extensions#63 (submodule bump included here).
Verified with GEN=Ninja make shell EXTENSION_STATIC_LINK_LIST='httpfs':
the link succeeds, libmbedtls.a exports zero plain _mbedtls_* symbols,
and the shell passes a smoke query.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant