Resolving SQLITE_API Export Issues When Building Custom SQLite DLL on Windows
Missing SQLite Function Exports in Custom Windows DLL Builds
Issue Overview: EntryPointNotFoundException Due to Unexported SQLite Functions
When attempting to build a custom SQLite DLL for Windows with extensions like SESSION and R-Tree, developers often encounter an EntryPointNotFoundException
referencing sqlite3_libversion_number
or other core API functions. This occurs when the compiled DLL fails to properly export SQLite’s public symbols, making them inaccessible to consuming applications like Unity projects or .NET wrappers. The root conflict stems from Windows’ strict symbol visibility rules conflicting with SQLite’s default build configuration.
Unlike Unix-like systems where global symbols are exported by default, Windows requires explicit marking of exported functions using __declspec(dllexport)
attributes. SQLite’s source code uses the SQLITE_API
macro to decorate API functions, but this macro remains undefined in typical Linux/Unix builds where symbol visibility works differently. When cross-compiling for Windows without specifying SQLITE_API=__declspec(dllexport)
, the resulting DLL contains all API functions but doesn’t expose them through the module’s export table. This leads to loader failures when applications attempt to dynamically link against these "hidden" functions.
The size discrepancy between official prebuilt DLLs (2.15MB) and custom builds (1.25MB) arises from differences in compilation flags controlling debug symbols, optimization levels, and cross-module inlining. Official binaries often retain symbolic debugging information and use less aggressive size optimization to maximize compatibility across Windows versions, while developer builds using -O2
optimization strip unnecessary metadata.
Possible Causes: Windows DLL Export Misconfiguration and Build Flag Omissions
Three primary factors contribute to the missing function exports:
Undefined SQLITE_API Macro for Windows Exports
The SQLite codebase uses conditional compilation to handle platform-specific symbol visibility. Insqlite3.h
, theSQLITE_API
macro is defined as:#if defined(_WIN32) && !SQLITE_API # define SQLITE_API __declspec(dllimport) #endif
This default assumes the DLL is being consumed (hence
dllimport
), not built. When compiling the DLL itself, developers must override this with-DSQLITE_API=__declspec(dllexport)
to mark exports.Incomplete Linker Configuration for Symbol Exposure
Microsoft’s LINK.EXE requires either:- A module definition (.def) file explicitly listing exported functions
__declspec(dllexport)
attributes on exported functions/EXPORT
linker directives
Without any of these, the DLL’s export table remains empty. The official SQLite build system for Windows (Makefile.msc) uses a generated .def file containing all public API functions.
Compiler Optimization Flags Stripping Metadata
Aggressive optimization flags like/O1
(minimize size) or/O2
(maximize speed) can eliminate functions considered unused during static analysis. While SQLite’s source structure generally prevents this, combining/GL
(Whole Program Optimization) with/LTCG
(Link-time Code Generation) might inadvertently remove vital functions if the build process doesn’t properly mark API boundaries.
Troubleshooting Steps: Ensuring Proper Symbol Exportation in SQLite DLLs
Step 1: Verify Current DLL Export Table
Use Microsoft’s dumpbin
utility to inspect exported functions:
dumpbin /EXPORTS sqlite3.dll
If the output lacks SQLite API functions like sqlite3_open
or sqlite3_libversion_number
, symbol exporting is misconfigured.
Step 2: Apply Correct SQLITE_API Macro During Compilation
Modify the build command to explicitly define symbol export attributes:
cl -O2 -DSQLITE_API=__declspec(dllexport) -DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_RTREE -DSQLITE_DQS=0 -DSQLITE_ENABLE_COLUMN_METADATA sqlite3.c -link -dll -out:sqlite3.dll
This forces __declspec(dllexport)
on all API functions through the SQLITE_API
macro.
Step 3: Compare Against Official Build Configuration
Download the SQLite amalgamation source matching the prebuilt DLL version. The official Windows builds use Makefile.msc
, which:
- Generates
sqlite3.def
viamkkeywordhash.exe
- Passes
SQLITE_API=__declspec(dllexport)
for DLL builds - Uses these linker flags:
/DYNAMICBASE /NXCOMPAT /DEBUG /OPT:REF /OPT:ICF
Replicate this configuration by:
- Building
mkkeywordhash.exe
fromtool/mkkeywordhash.c
- Generating
sqlite3.def
:mkkeywordhash > sqlite3.def
- Including the .def file in linking:
cl ... sqlite3.c /link /DEF:sqlite3.def ...
Step 4: Address Size Discrepancies via Symbol Inclusion
To match the official DLL’s size characteristics:
- Remove size optimization (
/O2
) for debug-compatible builds - Preserve debug symbols with
/Z7
or/DEBUG:FASTLINK
- Disable function inlining across modules with
/Ob0
Example production build command:
cl -Od -DSQLITE_API=__declspec(dllexport) -DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_COLUMN_METADATA -Z7 -GS -guard:cf sqlite3.c -link -dll -def:sqlite3.def -out:sqlite3.dll
Step 5: Validate DLL Compatibility with Dependent Components
After rebuilding, test the DLL with a minimal C# wrapper:
using System.Runtime.InteropServices;
public class SQLiteTest {
[DllImport("sqlite3.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_libversion_number();
public static void Main() {
Console.WriteLine(sqlite3_libversion_number());
}
}
Successful execution confirms proper exports. If Unity persists with errors, verify:
- Architecture match (x64 DLL with x64 Unity build)
- DLL placement in
Assets/Plugins/x86_64
- Absence of conflicting SQLite installations in system directories
Final Build Command Template
For production-ready DLLs with session and R-Tree support:
cl -Od -DSQLITE_API=__declspec(dllexport) -DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK -DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_GEOPOLY -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_COLUMN_METADATA -DSQLITE_USE_URI=1 -DSQLITE_DQS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_THREADSAFE=1 -DSQLITE_MAX_MMAP_SIZE=268435456 -D_CRT_SECURE_NO_WARNINGS -Z7 -MD -GS -guard:cf -W3 -nologo sqlite3.c -link -dll -def:sqlite3.def -out:sqlite3.dll
This includes common extensions and security-hardening flags while maintaining debug symbol compatibility with .NET profilers and crash dumps.