Linker Namespace

The dynamic linker tackles two challenges in Treble VNDK design:

  • SP-HAL shared libraries and their dependencies, including VNDK-SP libraries, are loaded into framework processes. There should be some mechanisms to prevent symbol conflicts.
  • dlopen() and android_dlopen_ext() may introduce some run-time dependencies that are not visible at build-time and can be difficult to detect using static analysis.

These two challenges can be resolved by the linker namespace mechanism. The linker namespace mechanism is provided by the dynamic linker. It can isolate the shared libraries in different linker namespaces so that libraries with same library name but with different symbols won't conflict.

On the other hand, the linker namespace mechanism provides the flexibility so that some shared libraries may be exported by a linker namespace and used by another linker namespace. These exported shared libraries may become the application programming interfaces that are public to other programs while hiding their implementation details within their linker namespaces.

For example, /system/lib[64]/libcutils.so and /system/lib[64]/vndk-sp-${VER}/libutils.so are two shared libraries. These two libraries may have different symbols. They will be loaded into different linker namespaces so that framework modules can depend on /system/lib[64]/libcutils.so and SP-HAL shared libraries can depend on /system/lib[64]/vndk-sp-${VER}/libcutils.so.

On the other hand, /system/lib[64]/libc.so is an example of public libraries that is exported by a linker namespace and imported into many linker namespaces. The dependencies of /system/lib[64]/libc.so, such as libnetd_client.so, will be loaded into the namespace in which /system/lib[64]/libc.so resides. Other namespaces won't have accesses to those dependencies. This mechanism encapsulates the implementation details while providing the public interfaces.

How does it work?

The dynamic linker is responsible for loading the shared libraries specified in DT_NEEDED entries or the shared libraries specified by the argument of dlopen() or android_dlopen_ext(). In both cases, the dynamic linker will find the linker namespace where the caller resides and try to load the dependencies into the same linker namespace. If the dynamic linker cannot load the shared library into the specified linker namespace, it will ask the linked linker namespace for exported shared libraries.

Configuration file format

The configuration file format is based on the INI file format. A typical configuration file looks like:

dir.system = /system/bin
dir.vendor = /vendor/bin

[system]
additional.namespaces = sphal

namespace.default.isolated = true
namespace.default.search.paths = /system/${LIB}:/vendor/${LIB}
namespace.default.permitted.paths = /system/${LIB}:/vendor/${LIB}

namespace.sphal.isolated = true
namespace.sphal.visible = true
namespace.sphal.search.paths = /vendor/${LIB}
namespace.sphal.permitted.paths = /vendor/${LIB}
namespace.sphal.links = default
namespace.sphal.link.default.shared_libs = libc.so:libm.so

[vendor]
namespace.default.isolated = false
namespace.default.search.paths = /vendor/${LIB}:/system/${LIB}
namespace.default.permitted.paths = /vendor/${LIB}:/system/${LIB}

First, there are several dir.${section} properties at the beginning of ld.config.txt:

dir.${section} = /path/to/bin/directory

These properties decide which set of rules will be applied to the process. For example, if a main executable resides in /system/bin, the rules in the [system] section are applied. Similarly, if a main executable resides in /vendor/bin, the rules in the [vendor] section are applied.

Second, for each section, in addition to the default linker namespace, addition.namespaces specifies the extra linker namespaces (separated by comma) that will be created by the dynamic linker:

additional.namespaces = namespace1,namespace2,namespace3

In the example above, the dynamic linker creates two linker namespaces (default and sphal) for the executables in /system/bin.

Third, for each linker namespace, following properties can be configured:

namespace.${name}.search.paths = /path1/${LIB}:/path2/${LIB}
namespace.${name}.permitted.paths = /path1:/path2
namespace.${name}.isolated = true|false
namespace.${name}.links = namespace1,namespace2
namespace.${name}.link.${other}.shared_libs = lib1.so:lib2.so
namespace.${name}.link.${other}.allow_all_shared_libs = true
namespace.${name}.visible = true|false

namespace.${name}.search.paths denotes the directories that will be prepended to the library name. Directories are separated by colons. ${LIB} is a special placeholder. If the process is running a 32-bit executable, ${LIB} is substituted by lib. Similarly, if the process is running a 64-bit executable, ${LIB} is substituted by lib64.

In the example above, if a 64-bit executable in /system/bin links with libexample.so, the dynamic linker searches for /system/lib64/libexample.so first. If /system/lib64/libexample.so is not available, the dynamic linker searches for /vendor/lib64/libexample.so.

If namespace.${name}.isolated is true, the dynamic linker loads only the shared libraries in the directories specified in namespace.${name}.search.paths or the shared libraries under the directories specified in namespace.${name}.permitted.paths.

In the example above, a shared library that is loaded in the sphal linker namespace won't be able to link to shared libraries in /system/lib[64] because namespace.sphal.isolated is true and /system/lib[64] is in neither namespace.sphal.permitted.paths nor namespace.sphal.search.paths.

namespace.${name}.links specifies a comma-separated list of linker namespaces that the ${name} linker namespace links to.

In the example above, namespace.sphal.links specifies that the sphal linker namespace links to the default linker namespace.

namespace.${name}.link.${other}.shared_libs specifies the shared library names (separated by colons) that may utilize the fallback link. If a shared library can't be loaded into the ${name} linker namespace and its name is in namespace.${name}.link.${other}.shared_libs, the dynamic linker will try to import the library from the ${other} linker namespace.

In the example above, namespace.sphal.link.default.shared_libs specifies that libc.so and libm.so may be exported by the default linker namespace. If a shared library loaded in the sphal linker namespace links to libc.so and the dynamic linker cannot find libc.so in /vendor/lib[64], the dynamic linker will walk through the fallback link and find the libc.so exported by the default linker namespace.

If namespace.${name}.link.${other}.allow_all_shared_libs is true, all shared library names may utilize the fallback link. If a shared library can't be loaded into the ${name} linker namespace, the dynamic linker will try to import the library from the ${other} linker namespace.

If namespace.${name}.visible is true, the program will be able to obtain a linker namespace handle, which can be passed to android_dlopen_ext() later.

In the example above, the namespace.sphal.visible is true so that android_load_sphal_library() can explicitly ask the dynamic linker to load a shared library in the sphal linker namespace.

Linker namespace isolation

There are three configurations in ${android-src}/system/core/rootdir/etc. Depending on the value of PRODUCT_TREBLE_LINKER_NAMESPACES, BOARD_VNDK_VERSION, and BOARD_VNDK_RUNTIME_DISABLE in BoardConfig.mk, different configurations will be selected:

PRODUCT_TREBLE_
LINKER_NAMESPACES
BOARD_VNDK_
VERSION
BOARD_VNDK_
RUNTIME_DISABLE
Selected configuration VTS Requirement
true current empty ld.config.txt Mandatory for devices launched with Android P.
true ld.config.vndk_lite.txt Mandatory for devices launched with Android 8.x.
empty any
false any any ld.config.legacy.txt For non-Treble devices

${android-src}/system/core/rootdir/etc/ld.config.vndk_lite.txt isolates SP-HAL and VNDK-SP shared libraries. In Android 8.0 and higher, this must be the configuration file for dynamic linker when PRODUCT_TREBLE_LINKER_NAMESPACES is true.

${android-src}/system/core/rootdir/etc/ld.config.txt isolates SP-HAL and VNDK-SP shared libraries as well. In addition, ld.config.txt also provides the full dynamic linker isolation. It makes sure that modules in the system partition won't depend on the shared libraries in the vendor partitions and vice versa.

In Android 8.1, ld.config.txt is the default configuration file and it is highly recommended to enable full dynamic linker isolation. However, if there are too many dependencies to be cleaned up in Android 8.1, you may add BOARD_VNDK_RUNTIME_DISABLE to BoardConfig.mk:

BOARD_VNDK_RUNTIME_DISABLE := true

If BOARD_VNDK_RUNTIME_DISABLE is true, ${android-src}/system/core/rootdir/etc/ld.config.vndk_lite.txt will be installed.

ld.config.txt

ld.config.txt isolates the shared library dependencies between the system partition and vendor partitions. Compared to ld.config.txt mentioned in previous subsection, the differences are outlined as following items:

  • Framework Processes

    • Four namespaces (default, vndk, sphal, and rs) are created.
    • All namespaces are isolated.
    • System shared libraries are loaded into the default namespace.
    • SP-HALs are loaded into the sphal namespace.
    • VNDK-SP shared libraries loaded into the vndk namespace.
  • Vendor Processes

    • Three namespaces (default, vndk, and system) are created.
    • The default namespace is isolated.
    • Vendor shared libraries are loaded into the default namespace.
    • VNDK and VNDK-SP shared libraries are loaded into the vndk namespace.
    • LL-NDK and their dependencies are loaded into the system namespace.

The relationship between the linker namespaces is depicted in the figure below:

Linker namespace graph described in ld.config.txt
Figure 1. Linker namespace isolation (ld.config.txt)

In the graph above, LL-NDK and VNDK-SP stand for following shared libraries:

  • LL-NDK
    • libEGL.so
    • libGLESv1_CM.so
    • libGLESv2.so
    • libGLESv3.so
    • libandroid_net.so
    • libc.so
    • libdl.so
    • liblog.so
    • libm.so
    • libnativewindow.so
    • libneuralnetworks.so
    • libsync.so
    • libvndksupport.so
    • libvulkan.so
  • VNDK-SP
    • android.hardware.graphics.common@1.0.so
    • android.hardware.graphics.mapper@2.0.so
    • android.hardware.renderscript@1.0.so
    • android.hidl.memory@1.0.so
    • libRSCpuRef.so
    • libRSDriver.so
    • libRS_internal.so
    • libbase.so
    • libbcinfo.so
    • libc++.so
    • libcutils.so
    • libhardware.so
    • libhidlbase.so
    • libhidlmemory.so
    • libhidltransport.so
    • libhwbinder.so
    • libion.so
    • libutils.so
    • libz.so

The table below presents the namespaces configuration for framework processes, which is excerpted from the [system] section in ld.config.txt:

Namespace Property Value
default search.paths /system/${LIB}
/product/${LIB}
permitted.paths /system/${LIB}/drm
/system/${LIB}/extractors
/system/${LIB}/hw
/product/${LIB}
/system/framework
/system/app
/system/priv-app
/vendor/app
/vendor/priv-app
/oem/app
/odm/priv-app
/oem/app
/product/framework
/product/app
/product/priv-app
/data
/mnt/expand
isolated true
sphal search.paths /odm/${LIB}
/vendor/${LIB}
permitted.paths /odm/${LIB}
/vendor/${LIB}
isolated true
visible true
links default,vndk,rs
link.default.shared_libs LL-NDK
link.vndk.shared_libs VNDK-SP
link.rs.shared_libs libRS_internal.so
vndk (For VNDK-SP) search.paths /odm/${LIB}/vndk-sp
/vendor/${LIB}/vndk-sp
/system/${LIB}/vndk-sp-${VER}
permitted.paths /odm/${LIB}/hw
/odm/${LIB}/egl
/vendor/${LIB}/hw
/vendor/${LIB}/egl
/system/${LIB}/vndk-sp-${VER}/hw
isolated true
visible true
links default, sphal
link.default.shared_libs LL-NDK
link.default.allow_all_shared_libs true
rs (For Renderscript) search.paths /odm/${LIB}/vndk-sp
/vendor/${LIB}/vndk-sp
/system/${LIB}/vndk-sp-${VER}
/odm/${LIB}
/vendor/${LIB}
permitted.paths /odm/${LIB}
/vendor/${LIB}
/data (For compiled RS kernel)
isolated true
visible true
links default,vndk
link.default.shared_libs LL-NDK
libmediandk.so
libft2.so
link.vndk.shared_libs VNDK-SP

The table below presents the namespaces configuration for vendor processes, which is excerpted from the [vendor] section in ld.config.txt:

Namespace Property Value
default search.paths /odm/${LIB}
/vendor/${LIB}
permitted.paths /odm
/vendor
isolated true
visible true
links system, vndk
link.system.shared_libs LL-NDK
link.vndk.shared_libs VNDK, VNDK-SP (vendor available)
vndk search.paths /odm/${LIB}/vndk
/odm/${LIB}/vndk-sp
/vendor/${LIB}/vndk
/vendor/${LIB}/vndk-sp
/system/${LIB}/vndk-${VER}
/system/${LIB}/vndk-sp-${VER}
isolated true
links system, default
link.system.shared_libs LL-NDK
link.default.allow_all_shared_libs true
system search.paths /system/${LIB}
isolated false

More details can be found in ${android-src}/system/core/rootdir/etc/ld.config.txt.

ld.config.vndk_lite.txt

As of Android 8.0, the dynamic linker is configured to isolate SP-HAL and VNDK-SP shared libraries such that their symbols do not conflict with other framework shared libraries. The relationship between the linker namespaces is shown below:

Linker namespace graph described in ld.config.vndk_lite.txt
Figure 2. Linker namespace isolation (ld.config.vndk_lite.txt)

LL-NDK and VNDK-SP stand for following shared libraries:

  • LL-NDK
    • libEGL.so
    • libGLESv1_CM.so
    • libGLESv2.so
    • libc.so
    • libdl.so
    • liblog.so
    • libm.so
    • libnativewindow.so
    • libstdc++.so (Not in ld.config.txt)
    • libsync.so
    • libvndksupport.so
    • libz.so (Moved to VNDK-SP in ld.config.txt)
  • VNDK-SP
    • android.hardware.graphics.common@1.0.so
    • android.hardware.graphics.mapper@2.0.so
    • android.hardware.renderscript@1.0.so
    • android.hidl.memory@1.0.so
    • libbase.so
    • libc++.so
    • libcutils.so
    • libhardware.so
    • libhidlbase.so
    • libhidlmemory.so
    • libhidltransport.so
    • libhwbinder.so
    • libion.so
    • libutils.so

The table below presents the namespaces configuration for framework processes, which is excerpted from the [system] section in ld.config.vndk_lite.txt:

Namespace Property Value
default search.paths /system/${LIB}
/odm/${LIB}
/vendor/${LIB}
/product/${LIB}
isolated false
sphal search.paths /odm/${LIB}
/vendor/${LIB}
permitted.paths /odm/${LIB}
/vendor/${LIB}
isolated true
visible true
links default,vndk,rs
link.default.shared_libs LL-NDK
link.vndk.shared_libs VNDK-SP
link.rs.shared_libs libRS_internal.so
vndk (For VNDK-SP) search.paths /odm/${LIB}/vndk-sp
/vendor/${LIB}/vndk-sp
/system/${LIB}/vndk-sp-${VER}
permitted.paths /odm/${LIB}/hw
/odm/${LIB}/egl
/vendor/${LIB}/hw
/vendor/${LIB}/egl
/system/${LIB}/vndk-sp-${VER}/hw
isolated true
visible true
links default
link.default.shared_libs LL-NDK
rs (For Renderscript) search.paths /odm/${LIB}/vndk-sp
/vendor/${LIB}/vndk-sp
/system/${LIB}/vndk-sp-${VER}
/odm/${LIB}
/vendor/${LIB}
permitted.paths /odm/${LIB}
/vendor/${LIB}
/data (For compiled RS kernel)
isolated true
visible true
links default,vndk
link.default.shared_libs LL-NDK
libmediandk.so
libft2.so
link.vndk.shared_libs VNDK-SP

The table below presents the namespaces configuration for vendor processes, which is excerpted from the [vendor] section in ld.config.vndk_lite.txt:

Namespace Property Value
default search.paths /odm/${LIB}
/odm/${LIB}/vndk
/odm/${LIB}/vndk-sp
/vendor/${LIB}
/vendor/${LIB}/vndk
/vendor/${LIB}/vndk-sp
/system/${LIB}/vndk-${VER}
/system/${LIB}/vndk-sp-${VER}
/system/${LIB} (Deprecated)
/product/${LIB} (Deprecated)
isolated false

More details can be found in ${android-src}/system/core/rootdir/etc/ld.config.vndk_lite.txt.

Document history

Android P changes

  • In Android P, the vndk linker namespace is added to vendor processes and VNDK shared libraries are isolated from the default linker namespace.

  • Replace PRODUCT_FULL_TREBLE with more specific PRODUCT_TREBLE_LINKER_NAMESPACES.

  • Android P changes the names of the following dynamic linker configuration files:

    Android 8.x Android P Description
    ld.config.txt.in ld.config.txt For devices with runtime linker namespace isolation
    ld.config.txt ld.config.vndk_lite.txt For devices with VNDK-SP linker namespace isolation
    ld.config.legacy.txt ld.config.legacy.txt For legacy devices running Android 7.x and earlier
  • Remove android.hardware.graphics.allocator@2.0.so.

  • product and odm partitions are added.