The Android dynamic linker (Documentation Index
Fetch the complete documentation index at: https://mintlify.com/android/ndk/llms.txt
Use this file to discover all available pages before exploring further.
linker for 32-bit, linker64 for 64-bit) is responsible for loading shared libraries (.so files) and resolving symbols at runtime. Understanding how it works is essential for debugging loading issues and optimizing your native code.
How the dynamic linker works
When your application loads a native library, the dynamic linker:- Locates the library - Searches for the
.sofile in the application’s library path - Loads dependencies - Recursively loads any libraries marked as
NEEDEDin the ELF header - Resolves symbols - Maps function and variable references to their implementations
- Executes initialization - Runs any constructors and initialization functions
Library search paths
The dynamic linker searches for libraries in this order:DT_RUNPATHorDT_RPATH(if set in the ELF file)LD_LIBRARY_PATHenvironment variable (not available for normal apps)- The application’s native library directory (e.g.,
/data/app/<package>/lib/arm64) - System library directories (
/system/lib64,/vendor/lib64)
Loading shared libraries
Static dependencies (NEEDED entries)
Libraries can declare static dependencies usingNEEDED entries in their ELF header. These are automatically loaded when the parent library loads:
Dynamic loading with dlopen()
You can load libraries at runtime usingdlopen():
dlopen() flags
RTLD_NOW- Resolve all symbols immediately (recommended)RTLD_LAZY- Defer symbol resolution until needed (can cause crashes later)RTLD_LOCAL- Don’t make symbols available to subsequently loaded librariesRTLD_GLOBAL- Make symbols available globally (use carefully)
Symbol resolution and visibility
Symbol visibility
Control which symbols are exported from your library:-Wl,--version-script=version_script.txt
Symbol interposition
When multiple libraries export the same symbol, the first one loaded wins. This can cause unexpected behavior:Linker namespaces
Starting with Android 7.0 (API level 24), the dynamic linker uses namespaces to isolate system libraries from application libraries. This prevents apps from using private system APIs.Namespace restrictions
- Apps cannot load non-public system libraries
- System libraries are in a separate namespace from app libraries
- Attempting to load restricted libraries results in errors
Common loading issues
Missing dependencies
- Ensure all dependencies are packaged in your APK
- Check for ABI mismatches (mixing 32-bit and 64-bit libraries)
- Verify library names in
NEEDEDentries match actual filenames
Symbol not found
- Verify the symbol exists:
nm -D libexample.so | grep SomeFunction - Check for C++ name mangling issues (use
extern "C") - Ensure the library exporting the symbol is loaded first
Text relocations
Performance considerations
Minimize library count
Each library has overhead:- Load time
- Memory for ELF headers and tables
- Symbol lookup time
Use symbol versioning
Symbol versioning allows you to maintain ABI compatibility while changing implementations:Preload critical libraries
Load frequently-used libraries at app startup to avoid delays later:Debugging linker issues
Enable linker debugging
Set environment variables to get detailed linker logs:Check library dependencies
Additional resources
For more detailed information about dynamic linker changes across Android versions:- Android changes for NDK developers - Essential reading for understanding linker behavior changes
- Bionic status documentation - API availability and behavior changes