Firebird's client library, libfbclient / fbclient.dll, has always been distributed exclusively as a shared library. PR #9104 by Adriano dos Santos Fernandes, merged into the Firebird source tree on July 24, 2026, changes that: it introduces an optional, non-default static archive (libfbclient.a on POSIX, fbclient_static.lib on Windows) for applications that need to link the client library statically instead of loading it as a shared object/DLL.
Why static linking wasn't offered before
The blocker wasn't packaging — it was memory management. Firebird overrides the global C++ operators operator new, operator new[], operator delete, and operator delete[] (in src/common/classes/alloc.cpp) so that any bare, non-pool allocation inside Firebird is routed through its own internal memory pool.
In the shared library, this is harmless: a POSIX version script (and a Windows .def file) keeps those overridden symbols hidden, so an application linking against libfbclient.so or fbclient.dll keeps its own global allocator untouched.
A static archive has no such boundary. Its object files get merged directly into the host application at link time, so the linker would silently resolve the host's own bare new/delete calls to Firebird's internal definitions — hijacking the host application's memory allocation without any warning. That's a nasty, hard-to-diagnose class of bug, and it's exactly why a static build was never shipped.
The fix: rename every internal symbol at the archive level
Rather than hand-maintain a list of symbols to hide, the PR takes a systematic approach: every internal global symbol not part of Firebird's public API gets renamed with a __fbclient_ prefix as a post-processing step on the compiled archive. This covers the global operators, decNumber symbols, C++ mangled names, vtables, typeinfo, guard variables — everything — generated automatically from the existing export list rather than curated by hand.
On POSIX (Linux and macOS), this happens in two steps:
- Archive slimming —
ld -rwith-uflags seeded from every symbol inbuilds/posix/firebird.verspulls in only the archive members actually reachable from the public API, using cross-reference output (--crefon ELF,-mapon Darwin) to identify and repack just those members. - Symbol renaming —
objcopy --redefine-syms(GNUobjcopyon Linux,llvm-objcopyon macOS) renames every non-API global symbol to a__fbclient_-prefixed name, using a rename map generated automatically vianm --defined-only -g.
Both platforms share a single helper script, static_client.sh (autoconf-generated from builds/posix/static_client.sh.in), differing only in which cross-reference flag ld uses.
On Windows, the equivalent pipeline runs via a new fix_fbclient_static.bat: it parses builds/win32/defs/firebird.def for the public API, runs llvm-nm on every object file to collect defined symbols, builds the same kind of rename map (preserving __stdcall @n decoration where relevant), and applies it with llvm-objcopy --redefine-syms.
Regular pool-based allocation via FB_NEW / FB_NEW_POOL — the pattern used throughout the Firebird codebase — was never affected by any of this, since it never touched the global operators in the first place.
A side effect: no DllMain on Windows
A statically linked fbclient has no separate DLL module, so DllMain (previously in src/jrd/os/win32/ibinitdll.cpp) never runs for it. Most of what DllMain did turns out to degrade safely without it — config-root lookup falls back to the host executable's path, and loader-lock hazard checks simply don't apply to a statically linked EXE.
The one real gap was per-thread cleanup, which used to run only from DllMain's DLL_THREAD_DETACH notification. The PR closes it with a new ThreadCleanup class (src/common/classes/ThreadCleanup.h/.cpp) that uses Fiber-Local Storage (FlsAlloc / FlsSetValue / FlsFree) on Windows — mirroring the destructor semantics pthread_key_create already gives the POSIX build for free, and working identically whether the code ends up in a DLL or linked directly into the host binary.
Building it
POSIX:
make -C temp/debug TARGET=Debug client_static
produces <firebird>/lib/libfbclient.a from the same object set as the shared library (yValve + remote client + common). Most third-party dependencies (tommath, tomcrypt) still need to be linked separately by the consuming application; decNumber/libdecFloat is the one exception — it's small and vendored in-tree, so it's merged directly into the archive.
Windows:
builds\win32\make_all.bat CLIENT_ONLY=STATIC
builds yvalve as fbclient_static.lib under new DebugStatic / ReleaseStatic configurations, then runs the symbol-fixup script automatically.
Verification is straightforward with nm/llvm-nm: the public isc_* API stays global, while global operators (_Znwm, _ZdlPv, etc.) and internal symbols like decNumberFromString disappear from their original names entirely — replaced by __fbclient_-prefixed equivalents.
What's in the diff
25 files changed, +2168/-268 lines, across three commits:
.github/workflows/static-build.yml— new manual CI workflows to build and validate the static client on Linux, macOS, and Windows.builds/posix/static_client.sh.in,builds/win32/fix_fbclient_static.bat,builds/win32/compile_static.bat— the new post-processing pipeline.builds/win32/msvc15/*.vcxproj*,*.props— new Debug/Release static configurations wired into the Visual Studio solution.src/common/classes/ThreadCleanup.h/.cpp— the new FLS-based thread cleanup, replacing the oldDLL_THREAD_DETACHpath.src/yvalve/MasterImplementation.cpp,utl.cpp,src/common/os/win32/mod_loader.cpp,src/jrd/os/win32/ibinitdll.cpp— call sites updated for the DLL-less code path.doc/README.StaticClient.md— new, thorough documentation covering the rationale, build steps, and verification commands for both platforms.
For any project embedding Firebird's client library directly into a host binary, this closes a real gap — and it does it without punting the symbol-collision risk onto the integrator.
Source: FirebirdSQL/firebird PR #9104
No comments:
Post a Comment