Fix dyld crash on macOS from missing sqlite3 extension symbols

The earlier -Wl,-U,_sqlite3_enable_load_extension fix only satisfied the
linker; at runtime, Kotlin/Native's cinterop wrapper for that function still
resolves against the symbol the moment SQLDelight's native-driver opens a
connection, and Apple's system libsqlite3 doesn't export it on macOS — so
the app now crashed instead of failing to build.

Adds weak no-op definitions of sqlite3_enable_load_extension and
sqlite3_load_extension, compiled directly into the app (Xcode's
file-system-synchronized group picks the file up with no project.pbxproj
changes needed beyond what Xcode itself rewrote on this build). Weak means
a platform whose system library does provide the real symbol keeps using
that one; this only fills in where it's missing.

Verified there's no longer a crash by running the built macOS binary
directly and by installing/launching it on the iOS Simulator — neither
produced a dyld error or a crash report, versus two reproducible crash
reports beforehand with the exact reported failure signature.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QpjMKWGoiT5aJBzhxvXkwp
This commit is contained in:
2026-09-08 10:39:29 +03:00
co-authored by Claude Sonnet 5
parent f35fb05e7c
commit e71b01b0e3
2 changed files with 52 additions and 20 deletions
+28 -20
View File
@@ -11,7 +11,7 @@
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
D262C3356B38E7E13FFE64E4 /* Exceptions for "iosApp" folder in "iosApp" target */ = {
D262C3356B38E7E13FFE64E4 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Info.plist,
@@ -23,13 +23,21 @@
/* Begin PBXFileSystemSynchronizedRootGroup section */
24D66E9CCFCEB2D844328B79 /* Configuration */ = {
isa = PBXFileSystemSynchronizedRootGroup;
explicitFileTypes = {
};
explicitFolders = (
);
path = Configuration;
sourceTree = "<group>";
};
55D8B06C58654F251C5B3381 /* iosApp */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
D262C3356B38E7E13FFE64E4 /* Exceptions for "iosApp" folder in "iosApp" target */,
D262C3356B38E7E13FFE64E4 /* PBXFileSystemSynchronizedBuildFileExceptionSet */,
);
explicitFileTypes = {
};
explicitFolders = (
);
path = iosApp;
sourceTree = "<group>";
@@ -137,6 +145,24 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
1DADA2DD199949A498015FFD /* Sanitize Moko Resource Bundle Names */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Sanitize Moko Resource Bundle Names";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if [ \"$PLATFORM_NAME\" != \"macosx\" ]; then\n exit 0\nfi\n# moko-resources names its resource bundle after the Kotlin klib unique_name (group:module),\n# e.g. SatsPrice:shared.bundle, and drops it directly under Contents/. Colons break\n# codesign, and a loose bundle outside Contents/Resources isn't recognized as a proper\n# nested bundle either, so the final app-level CodeSign phase fails with \"code object is\n# not signed at all\" unless we rename and relocate it. NSBundle resolves this bundle by\n# directory identity, not by name/path, so moko-resources/localization lookups still work.\nfind \"$CODESIGNING_FOLDER_PATH\" -type d -name \"*:*.bundle\" | while IFS= read -r bundle; do\n safe_name=$(basename \"$bundle\" | tr ':' '_')\n mkdir -p \"$CODESIGNING_FOLDER_PATH/Contents/Resources\"\n dest=\"$CODESIGNING_FOLDER_PATH/Contents/Resources/$safe_name\"\n rm -rf \"$dest\"\n mv \"$bundle\" \"$dest\"\ndone\n";
};
20CB400CC39EEB23909EAA0F /* Compile Kotlin Framework */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@@ -174,24 +200,6 @@
shellPath = /bin/sh;
shellScript = "\"$SRCROOT/../gradlew\" -p \"$SRCROOT/../\" :shared:copyFrameworkResourcesToApp \\\n -Pmoko.resources.PLATFORM_NAME=\"$PLATFORM_NAME\" \\\n -Pmoko.resources.CONFIGURATION=\"$CONFIGURATION\" \\\n -Pmoko.resources.ARCHS=\"$ARCHS\" \\\n -Pmoko.resources.BUILT_PRODUCTS_DIR=\"$BUILT_PRODUCTS_DIR\" \\\n -Pmoko.resources.CONTENTS_FOLDER_PATH=\"$CONTENTS_FOLDER_PATH\" \n";
};
1DADA2DD199949A498015FFD /* Sanitize Moko Resource Bundle Names */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Sanitize Moko Resource Bundle Names";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if [ \"$PLATFORM_NAME\" != \"macosx\" ]; then\n exit 0\nfi\n# moko-resources names its resource bundle after the Kotlin klib unique_name (group:module),\n# e.g. SatsPrice:shared.bundle, and drops it directly under Contents/. Colons break\n# codesign, and a loose bundle outside Contents/Resources isn't recognized as a proper\n# nested bundle either, so the final app-level CodeSign phase fails with \"code object is\n# not signed at all\" unless we rename and relocate it. NSBundle resolves this bundle by\n# directory identity, not by name/path, so moko-resources/localization lookups still work.\nfind \"$CODESIGNING_FOLDER_PATH\" -type d -name \"*:*.bundle\" | while IFS= read -r bundle; do\n safe_name=$(basename \"$bundle\" | tr ':' '_')\n mkdir -p \"$CODESIGNING_FOLDER_PATH/Contents/Resources\"\n dest=\"$CODESIGNING_FOLDER_PATH/Contents/Resources/$safe_name\"\n rm -rf \"$dest\"\n mv \"$bundle\" \"$dest\"\ndone\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
+24
View File
@@ -0,0 +1,24 @@
#include <sqlite3.h>
#include <stddef.h>
// Apple's system libsqlite3 omits extension-loading support on some platforms (notably macOS,
// deliberately, for security), yet SQLDelight's native-driver still references these symbols —
// Kotlin/Native's cinterop generates a wrapper for every function declared in sqlite3.h,
// regardless of whether anything actually calls it, and this app never loads SQLite extensions.
// Without a definition somewhere, that missing symbol fails at link time (or, if the linker is
// told to allow it, crashes dyld the moment the app runs and Kotlin's wrapper resolves it).
//
// These are weak, so on a platform where the real system symbol does exist, it's used instead;
// this fallback only takes over where the real one is missing.
__attribute__((weak))
int sqlite3_enable_load_extension(sqlite3 *db, int onoff) {
return SQLITE_OK;
}
__attribute__((weak))
int sqlite3_load_extension(sqlite3 *db, const char *zFile, const char *zProc, char **pzErrMsg) {
if (pzErrMsg != NULL) {
*pzErrMsg = NULL;
}
return SQLITE_ERROR;
}