Linking Issues with Native Assets

This document describes common linking issues that can occur when using native assets in Dart and provides workarounds.

Conflicting with Embedder Library Dependencies

When a native asset provides a library that is also a dependency of the embedder, you may encounter runtime crashes. This is because the symbols from the already-loaded library can conflict with the symbols from the native asset.

This can happen, for example, when using a hook that bundles sqlite3 in a Flutter application on Linux. The Flutter embedder already depends on libsqlite3.so, so when the Dart code tries to load the sqlite3 native asset, the dynamic linker can get confused and resolve symbols to the wrong library, leading to a crash.

This issue only happens on Linux, as the other OSes use some kind of namespacing for symbols by default. On Linux, trying to use namespacing leads to issues with using sanitizers, so on Linux Dart and Flutter do not enable namespacing. See GitHub issue #2724 for more details.

Workaround 1: Symbol Prefixing

Create a wrapper library that statically links the conflicting library (e.g., SQLite) and re-exposes its symbols with a unique prefix. This avoids name collisions with the library loaded by the embedder.

If your library enables end-users to load either a custom native library or one from the operating system via a user-define, you can design the wrapper to dynamically link against that library while still re-exposing the symbols with a prefix. This prevents having to duplicate the @Native bindings for supporting that user-define.

Workaround 2: Symbol Versioning and Export Maps

Use ffigen to generate a list of symbols that need to be accessed from Dart. Then, use the -Wl,--version-script= linker flag with a script that marks only those necessary symbols as global and all others as local. This reduces the list of symbols that could conflict with the embedder. If one of the required symbols clashes with the embedder, this method doesn't work.

Workaround 3: -Bsymbolic Linker Flag

Adding DF_SYMBOLIC as an ELF dynamic entry via the -Wl,-Bsymbolic linker flag can resolve conflicts within a single dynamic library. This flag tells the dynamic linker to resolve symbol references within the library first, before searching in other libraries. This is similar in effect to using the RTLD_DEEPBIND flag with dlopen.