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/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/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 out the linker namespace in which the caller resides in 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 in 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 the main executable is in /system/bin, the rules in the [system] are applied. Similarly, if the main executable is in /vendor/bin, the rules in [vendor] 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}.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 links two linker namespaces and 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}.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_FULL_TREBLE, BOARD_VNDK_VERSION, and BOARD_VNDK_RUNTIME_DISABLE in BoardConfig.mk, different configurations will be selected:

PRODUCT_FULL_TREBLE BOARD_VNDK_VERSION / BOARD_VNDK_RUNTIME_DISABLE Selected configuration
false any ld.config.legacy.txt
true current and empty ld.config.txt.in
empty or true ld.config.txt

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

android-src/system/core/rootdir/etc/ld.config.txt.in isolates SP-HAL and VNDK-SP shared libraries as well. In addition, ld.config.txt.in 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.in is the default configuration 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.txt will be installed.

ld.config.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.txt
Figure 2. Linker namespace isolation (ld.config.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.in)
    • libsync.so
    • libvndksupport.so
    • libz.so (Moved to VNDK-SP in ld.config.txt.in)
  • VNDK-SP
    • android.hardware.graphics.common@1.0.so
    • android.hardware.graphics.allocator@2.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.txt:

Namespace Property Value
default search.paths /system/${LIB}
/vendor/${LIB}
permitted.paths /system/${LIB}
/vendor/${LIB}
isolated false
sphal search.paths /vendor/${LIB}/egl
/vendor/${LIB}/hw
/vendor/${LIB}
permitted.paths /vendor/${LIB}
/system/${LIB}/vndk-sp/hw (Android 8.1)
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 /vendor/${LIB}/vndk-sp
/system/${LIB}/vndk-sp
/vendor/${LIB}
permitted.paths /vendor/${LIB}/egl
/vendor/${LIB}/hw
isolated true
visible true
links default
link.default.shared_libs LL-NDK
rs (For Renderscript) search.paths /vendor/${LIB}/vndk-sp
/system/${LIB}/vndk-sp
/vendor/${LIB}
permitted.paths /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 /vendor/${LIB}
/vendor/${LIB}/vndk-sp
/system/${LIB}/vndk-sp
/system/${LIB} (Deprecated)
isolated false

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

ld.config.txt.in

ld.config.txt.in 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
    • The default namespace is isolated. A shared library can be loaded into the default namespace only if it is in a directory specified in the search paths or under a directory specified in the permitted paths.
    • The permitted paths of the default namespace have been changed to a limited set (/vendor/lib[64], /system/lib[64]/vndk, and /system/lib[64]/vndk-sp have been excluded).
  • Vendor Processes
    • Two namespaces (default and system) are created.
    • The default namespace is isolated. A shared library can be loaded into the default namespace only if it is in a directory specified in the search paths or under a directory specified in the permitted paths.
    • The permitted paths of the default namespace are /vendor, /system/lib[64]/vndk, and /system/lib[64]/vndk-sp.
    • The default namespace and system namespace are linked. The default namespace may link to LL-NDK libraries loaded in the system namespace.

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

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

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
    • libsync.so
    • libvndksupport.so
  • VNDK-SP
    • android.hardware.graphics.common@1.0.so
    • android.hardware.graphics.allocator@2.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.in:

Namespace Property Value
default search.paths /system/${LIB}
permitted.paths /system/${LIB}/drm
/system/${LIB}/hw
/system/framework
/system/app
/system/priv-app
/vendor/app
/vendor/framework
/oem/app
/data
/mnt/expand
isolated true
sphal search.paths /vendor/${LIB}/egl
/vendor/${LIB}/hw
/vendor/${LIB}
permitted.paths /vendor/${LIB}
/system/${LIB}/vndk-sp/hw
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 /vendor/${LIB}/vndk-sp
/system/${LIB}/vndk-sp
permitted.paths /vendor/${LIB}/egl
/vendor/${LIB}/hw
isolated true
visible true
links default
link.default.shared_libs LL-NDK
rs (For Renderscript) search.paths /vendor/${LIB}/vndk-sp
/system/${LIB}/vndk-sp
/vendor/${LIB}
permitted.paths /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.in:

Namespace Property Value
default search.paths /vendor/${LIB}/hw
/vendor/${LIB}/egl
/vendor/${LIB}
/vendor/${LIB}/vndk
/system/${LIB}/vndk
/vendor/${LIB}/vndk-sp
/system/${LIB}/vndk-sp
permitted.paths /vendor
/system/${LIB}/vndk
/system/${LIB}/vndk-sp
isolated true
links system
link.system.shared_libs LL-NDK
system search.paths /system/${LIB}
permitted.paths /system/${LIB}

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