-
Notifications
You must be signed in to change notification settings - Fork 230
Unprefixed global symbols conflict with other native libraries #618
Description
Description
rive_native exposes a large number of unprefixed global C/C++ symbols (functions, types, constants) in its compiled shared library. When an app links rive_native alongside other native libraries (e.g., via FFI, other Flutter plugins, or platform channels), these generic symbol names collide, causing linker errors or undefined runtime behavior.
Problem
Symbols like common names (e.g., decode, flush, lerp, Mat2D, Vec2D, Factory, Renderer, etc.) are not namespaced with any library-specific prefix. On platforms with flat symbol namespaces (iOS, Android NDK with certain linker settings), this leads to:
- Link-time failures - duplicate symbol errors when two libraries export the same name.
- Runtime crashes - the dynamic linker silently binds to the wrong implementation, causing subtle corruption or segfaults.
This makes it difficult - and in some configurations impossible - to use rive_native in a project that also depends on other native code.
Reproduction
- Create a Flutter project that depends on
rive(which pulls inrive_native). - Add any other plugin or FFI binding that also exports common unqualified symbol names.
- Build for iOS or Android.
- Observe duplicate symbol / linker errors, or runtime crashes from symbol shadowing.
Note: We were able to reproduce this on iOS - the app just crashes. On Android it worked, but it might cause issues there too that we are just not aware of.
Suggested Fix
Prefix all public/exported C symbols with rive_ (or RiveNative_, etc.). For example:
| Before | After |
|---|---|
| decode(...) | rive_decode(...) |
| flush | rive_flush |
| Factory_create(...) | rive_factory_create(...) |
This is a widely adopted convention in the C/native ecosystem (e.g., sk_ for Skia, hb_ for HarfBuzz, ft_ for FreeType) specifically to avoid this class of problem.
For C++ symbols that are exported with C linkage (extern "C"), the same prefixing should apply. Internal symbols that don't need to be exported should be hidden using visibility attributes (__attribute__((visibility("hidden"))) / -fvisibility=hidden) so they never enter the global symbol table in the first place.
Environment
- rive (Flutter): latest
- Platforms affected: iOS, Android (any platform with shared native linking)
Additional Context
This is a blocking issue for any project that integrates Rive alongside other native dependencies. A prefixing pass (potentially scripted) would be a one-time fix that permanently eliminates this category of conflict.
Thank you for considering this!