Deep Dive: Dynamic Loading
Dynamic loading allows an Android application or system service to load shared libraries (.so files) into memory at runtime, rather than during the initial startup sequence. This is critical for plugins, JNI (Java Native Interface), and modular system components.
dlopen(), dlsym(), dlclose()
The Bionic dynamic linker exposes the standard POSIX API for dynamic loading:
dlopen(const char* filename, int flags): Loads the specified shared library into the caller's address space. Theflagsparameter controls binding behavior (e.g.,RTLD_LAZYfor resolving symbols only when executed, orRTLD_NOWfor resolving everything immediately).dlsym(void* handle, const char* symbol): Looks up the memory address of a specific function or variable by name within a loaded library.dlclose(void* handle): Decrements the reference count of the library. If the count reaches zero, the library is unloaded from memory.
// Example: Loading a library dynamically via JNI
void* handle = dlopen("libcustom_plugin.so", RTLD_NOW);
if (!handle) {
ALOGE("Failed to load plugin: %s", dlerror());
return;
}
typedef int (*PluginFunc)();
PluginFunc my_func = (PluginFunc)dlsym(handle, "execute_plugin");
if (my_func) {
my_func();
}
dlclose(handle);
Linker Namespaces in Android
Historically, dlopen() could load any library present on the file system. To improve security and stability, Android introduced Linker Namespaces (starting in Android Nougat).
Linker namespaces isolate the dynamic linking environment. Every namespace contains:
- A specific set of allowed search paths (e.g.,
/data/app/). - A strict whitelist of libraries that are permitted to be loaded.
- Links to other namespaces (allowing controlled access to shared system libraries like
libc.so).
This prevents a malicious or buggy app from loading internal, unstable system libraries.
APEX Linker Namespaces
With the introduction of Project Mainline, Android packages core system components as APEX (Android Pony EXpress) modules. Since APEX modules can be updated independently of the OS, they require isolated linking environments.
Each APEX module gets its own linker namespace. If an APEX requires a library from the base system, it must traverse a strict namespace boundary. This ensures that an updated APEX module does not accidentally link against an incompatible, older version of a library located in /system/lib64.
Namespace Isolation for Vendor vs System
Project Treble mandated strict separation between the Android OS framework (/system) and the device-specific hardware implementations (/vendor). Linker namespaces enforce this boundary:
- System Namespace: Only allows loading libraries from
/system/lib64. - Vendor Namespace: Only allows loading libraries from
/vendor/lib64.
The two namespaces communicate solely through standardized, versioned VNDK (Vendor NDK) libraries. This isolation guarantees that framework updates do not break vendor hardware drivers.
# You can dump linker namespace configurations on a rooted device:
adb shell cat /linkerconfig/ld.config.txt