Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Failed to load dynamic library 'liblibsql_dart.so' on x86_64 Android #2

Open
Braden-Preston opened this issue Sep 1, 2024 · 0 comments · May be fixed by #3
Open

Failed to load dynamic library 'liblibsql_dart.so' on x86_64 Android #2

Braden-Preston opened this issue Sep 1, 2024 · 0 comments · May be fixed by #3

Comments

@Braden-Preston
Copy link

I am documenting an issue that I found with the library while testing it on an x84_64 Android simulator. After working with the developer, we have come up with a fix through a PR I will be submitting.

Original Issue:

braden@framework:~/Documents/GitHub/libsql_dart/example$ flutter run -d emulator-5554
Launching lib/main.dart on sdk gphone64 x86 64 in debug mode...
INFO: Precompiled binaries are disabled
INFO: Building libsql_dart for x86_64-linux-android
INFO: Building libsql_dart for i686-linux-android
INFO: Building libsql_dart for x86_64-linux-android
Running Gradle task 'assembleDebug'...                             11.5s
✓ Built build/app/outputs/flutter-apk/app-debug.apk
Installing build/app/outputs/flutter-apk/app-debug.apk...        1,694ms
E/flutter (15856): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Invalid argument(s): Failed to load dynamic library 'liblibsql_dart.so': dlopen failed: cannot locate symbol "__extenddftf2" referenced by "/data/app/~~-Di2SN8qoJQLpmCJ4R4Xmw==/com.example.libsql_dart_example-DyZPffNvgWuCSa4RNtAR9Q==/lib/x86_64/liblibsql_dart.so"...
E/flutter (15856): #0      _open (dart:ffi-patch/ffi_dynamic_library_patch.dart:11:43)
E/flutter (15856): #1      new DynamicLibrary.open (dart:ffi-patch/ffi_dynamic_library_patch.dart:22:12)
E/flutter (15856): #2      new ExternalLibrary.open (package:flutter_rust_bridge/src/platform_types/_io.dart:42:47)
E/flutter (15856): #3      loadExternalLibraryRaw (package:flutter_rust_bridge/src/loader/_io.dart:56:28)
E/flutter (15856): #4      loadExternalLibrary (package:flutter_rust_bridge/src/loader/_io.dart:14:10)
E/flutter (15856): #5      BaseEntrypoint._loadDefaultExternalLibrary (package:flutter_rust_bridge/src/main_components/entrypoint.dart:129:13)
E/flutter (15856): #6      BaseEntrypoint.initImpl (package:flutter_rust_bridge/src/main_components/entrypoint.dart:48:31)
E/flutter (15856): #7      RustLib.init (package:libsql_dart/src/rust/frb_generated.dart:33:20)
E/flutter (15856): #8      LibsqlClient.connect (package:libsql_dart/src/libsql_client.dart:71:21)
E/flutter (15856): #9      main (package:libsql_dart_example/main.dart:13:16)
E/flutter (15856): #10     _runMain.<anonymous closure> (dart:ui/hooks.dart:301:23)
E/flutter (15856): #11     _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
E/flutter (15856): #12     _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
E/flutter (15856): 
Syncing files to device sdk gphone64 x86 64... 
Future main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Create path for database
  final Directory appCacheDir = await getApplicationCacheDirectory();
  String dbPath = p.join(appCacheDir.path, "pw_data", "data.db");
  print(dbPath);

  // Open up embedded replica
  final client = LibsqlClient(dbPath)
    ..authToken = '*******************************************'
    ..syncUrl = 'libsql://************-braden-preston.turso.io'
    ..syncIntervalSeconds = 5
    ..readYourWrites = true;
  await client.connect();
  await client.sync();

  var result = await client.query('SELECT * FROM todos');
  print(result);

  await client.dispose();

  runApp(MyApp());
}

The Solution:

The issue I was having is also described in the GitHub issue for rust_flutter_bridge

fzyzcjy/flutter_rust_bridge#1844 (comment)

It occurs when you try to build for x86_64 Android, which is a common scenario when testing in an Android Simulator on Windows, Linux, or Intel-based Macs.

The original exception complained about not finding the __extenddftf2 symbol, so the loading of liblibsql_dart.so fails.

Exception has occurred.
ArgumentError (Invalid argument(s): Failed to load dynamic library 'liblibsql_dart.so': dlopen failed: cannot locate symbol "__extenddftf2" referenced by "/data/app/~~2LbsygFvEjeMsdspEE7DBg==/com.example.libsql_dart_example-Ixoad795pgrxUmEh2loEvA==/lib/x86_64/liblibsql_dart.so"...)

The fix was adding a build.rs file to my libsql_dart fork at {repo}/rust/build.rs that provides the correct linking, and setting the right env vars for Clang and NDK in the terminal. Here is an explanation from the Rust GitHub:

rust-lang/rust#109717 (comment)

The extenddftft2 symbol comes from the compilation of the function foo, for the extension of the 64 bits double to a 128-bits double. It is part of libclang_rt that comes with clang. But because rustc calls the linker (clang) with -nodefaultlibs, the clang runtime is not compiled in. And because the equivalent compiler-builtins from rustc doesn't contain all the clang builtins, this fails to link.

When linking dynamic libraries, the link actually goes through without an error, but yields an error when the library is loaded at runtime because of the missing extenddftft2 symbol. You'll find multiple people running into this problem over the years, but somehow I didn't find it reported against the rust compiler (example: mozilla/application-services#5436).

Because the __extenddftft2 symbol exists in rustc's compiler-builtins for aarch64 android, it's not a problem on that platform.

I've confirmed that this fix works on emulated x86_64 builds in Android, API versions 35, 34, and 29 - with Clang 18 and NDK version 27. It also continues to make the Linux build successfully on Ubuntu 22.04.

This has not been tested on Windows yet, though it should work. The patch to the rust build commands checks to make sure the build target is Android only, before applying the fix:

if target_arch == "x86_64" && target_os == "android" {
        let android_ndk_home = env::var("ANDROID_NDK_HOME").expect("ANDROID_NDK_HOME not set");
        ...
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 a pull request may close this issue.

1 participant