|
| 1 | +# Linking Issues with Native Assets |
| 2 | + |
| 3 | +This document describes common linking issues that can occur when using native |
| 4 | +assets in Dart and provides workarounds. |
| 5 | + |
| 6 | +## Conflicting with Embedder Library Dependencies |
| 7 | + |
| 8 | +When a native asset provides a library that is also a dependency of the |
| 9 | +embedder, you may encounter runtime crashes. This is because the symbols from |
| 10 | +the already-loaded library can conflict with the symbols from the native asset. |
| 11 | + |
| 12 | +This can happen, for example, when using a hook that bundles sqlite3 in a |
| 13 | +Flutter application on Linux. The Flutter embedder already depends on |
| 14 | +`libsqlite3.so`, so when the Dart code tries to load the `sqlite3` native asset, |
| 15 | +the dynamic linker can get confused and resolve symbols to the wrong library, |
| 16 | +leading to a crash. |
| 17 | + |
| 18 | +This issue only happens on Linux, as the other OSes use some kind of namespacing |
| 19 | +for symbols by default. On Linux, trying to use namespacing leads to issues with |
| 20 | +using sanitizers, so on Linux Dart and Flutter do not enable namespacing. See |
| 21 | +[GitHub issue #2724](https://github.com/dart-lang/native/issues/2724) for more |
| 22 | +details. |
| 23 | + |
| 24 | +### Workaround 1: Symbol Prefixing |
| 25 | + |
| 26 | +Create a wrapper library that statically links the conflicting library (e.g., |
| 27 | +SQLite) and re-exposes its symbols with a unique prefix. This avoids name |
| 28 | +collisions with the library loaded by the embedder. |
| 29 | + |
| 30 | +If your library enables end-users to load either a custom native library or one |
| 31 | +from the operating system via a user-define, you can design the wrapper to |
| 32 | +dynamically link against that library while still re-exposing the symbols with a |
| 33 | +prefix. This prevents having to duplicate the `@Native` bindings for supporting |
| 34 | +that user-define. |
| 35 | + |
| 36 | +### Workaround 2: Symbol Versioning and Export Maps |
| 37 | + |
| 38 | +Use `ffigen` to generate a list of symbols that need to be accessed from |
| 39 | +Dart. Then, use the `-Wl,--version-script=` linker flag with a script that |
| 40 | +marks only those necessary symbols as global and all others as local. This |
| 41 | +reduces the list of symbols that could conflict with the embedder. If one of the |
| 42 | +required symbols clashes with the embedder, this method doesn't work. |
| 43 | + |
| 44 | +### Workaround 3: `-Bsymbolic` Linker Flag |
| 45 | + |
| 46 | +Adding `DF_SYMBOLIC` as an ELF dynamic entry via the `-Wl,-Bsymbolic` linker |
| 47 | +flag can resolve conflicts within a single dynamic library. This flag tells |
| 48 | +the dynamic linker to resolve symbol references within the library first, |
| 49 | +before searching in other libraries. This is similar in effect to using the |
| 50 | +`RTLD_DEEPBIND` flag with `dlopen`. |
0 commit comments