KasperskyOS Community Edition 1.3

Using dynamic libraries

Dynamic libraries (*.so files) can be used in a KasperskyOS-based solution. Compared to static libraries (*.a files), dynamic libraries provide the following advantages:

  • Efficient use of RAM.

    Multiple processes can use the same instance of a dynamic library. Also, an executable file and dynamic libraries in one process can use the same instance of a dynamic library.

    Dynamic libraries can be loaded into memory and unloaded from memory on the initiative of the programs that use them.

  • Convenient software updates.

    A dynamic library update is applied to all executable files and dynamic libraries dependent on this dynamic library without having to rebuild them.

  • Capability to implement a mechanism for plug-ins.

    Plug-ins for solution components consist of dynamic libraries.

  • Shared use of code and data.

    One instance of a dynamic library can be concurrently used by multiple processes, and by an executable file and dynamic libraries in one process. This enables centralized management of multi-access to resources or storage of shared data, for example.

Dynamic libraries are provided in KasperskyOS Community Edition, and can also be created by a KasperskyOS-based solution developer. Normal operation of third-party dynamic libraries cannot be guaranteed.

Due to current technical limitations, libc.so and libpthread.so cannot be used in a KasperskyOS-based solution.

In this section

Prerequisites for using dynamic libraries

Life cycle of a dynamic library

Including the BlobContainer system program in a KasperskyOS-based solution

Building dynamic libraries

Adding dynamic libraries to a KasperskyOS-based solution

Page top
[Topic shared_libraries]

Prerequisites for using dynamic libraries

To use dynamic libraries in a KasperskyOS-based solution, the following conditions must be met:

  1. Processes that use dynamic libraries must have access to the file systems in which the files of the dynamic libraries are stored. Access to file systems is provided by VFS, which is a separate process. The dynamic libraries must not be used by VFS or other software that is used by VFS to work with storage (such as a storage driver).
  2. The system program BlobContainer must be included in the solution.
  3. Executable files that use dynamic libraries must be built with the -rdynamic flag (with dynamic linking).

    The CMake initialize_platform() command causes this flag to be used automatically for building all executable files defined via CMake add_executable() commands.

If the CMake command initialize_platform(FORCE_STATIC) is specified in the CMakeLists.txt root file, the toolchain performs static linking of executable files.

The CMake project_static_executable_header_default() command affects the build of executable files defined via subsequent CMake add_executable() commands in one CMakeLists.txt file. The toolchain performs static linking of these executable files.

The CMake platform_target_force_static() command affects the build of one executable file defined via the CMake add_executable() command. The toolchain performs static linking of this executable file.

The executable file that is built with the -rdynamic flag is linked to a static library if a dynamic library is not found. For example, if the CMake target_link_libraries(client -lm) command is being used, the executable file of the client program is linked to the static library libm.a if the dynamic library libm.so is not found.

Page top
[Topic shared_libraries_use_conditions]

Life cycle of a dynamic library

The life cycle of a dynamic library includes the following phases:

  1. Loading into memory.

    A dynamic library linked to an executable file is loaded into shared memory upon startup of the process that was created based on this executable file. A process can also load a dynamic library into shared memory by calling the dlopen() function of the POSIX interface. A dynamic library may be linked to other dynamic libraries, so an executable file depends not only on the dynamic library directly linked to it, but also depends on the entire dependency graph of this library. A dynamic library is loaded into shared memory together with all of the dynamic libraries that it depends on.

    One instance of a dynamic library is loaded into shared memory regardless of how many processes are using this library. (More specifically, only the part of the dynamic library that includes code and read-only data is loaded into shared memory. The other part of the dynamic library is loaded into the memory of each process that uses this library.) A dynamic library on which several other dynamic libraries depend is loaded into shared memory in a single instance.

    If a list of dynamic libraries is defined through the LD_PRELOAD environment variable, these dynamic libraries will be loaded into shared memory even if the executable file is not dependent on them. (List items must be absolute or relative paths to dynamic libraries separated by a colon, for example: LD_PRELOAD=libmalloc.so:libfree.so:/usr/somepath/lib/libfoo.so.) The functions that are exported by the dynamic libraries specified in LD_PRELOAD replace the identically named functions that are exported by other dynamic libraries. This can be used for debugging purposes if you need to replace functions imported from dynamic libraries.

    The dynamic library loader searches for dynamic libraries in the following order:

    1. Absolute paths defined through the LD_LIBRARY_PATH environment variable.

      Paths must be separated by a colon, for example: LD_LIBRARY_PATH=/usr/lib:/home/user/lib.

    2. Absolute paths defined in the DT_RUNPATH or DT_RPATH field of the .dynamic section of executable files and dynamic libraries.

      Linking of executable files and dynamic libraries may include defined paths that the dynamic library loader will search. (For example, this can be done through the INSTALL_RPATH property in the CMake command set_target_properties().) Paths used to search for dynamic libraries are stored in the DT_RUNPATH or DT_RPATH field of the .dynamic section. This field may be in executable files linked to dynamic libraries and in dynamic libraries linked to other dynamic libraries.

    3. Path /lib.

    The dynamic library loader searches in this same order if a relative path to a dynamic library is specified in the filename parameter of the dlopen() function or in the LD_PRELOAD environment variable. If the absolute path is specified, the loader puts the dynamic library into shared memory without performing a search.

  2. Use by a process (or processes).
  3. Unloading from memory.

    A dynamic library is unloaded from shared memory when all processes using this library have terminated or called the dlclose() function of the POSIX interface. A dynamic library linked to an executable file cannot be unloaded from shared memory until termination of the process that was created based on this executable file. A dynamic library that is linked to other dynamic libraries is unloaded from shared memory after all libraries that depend on it are unloaded.

Page top
[Topic shared_libraries_lifecycle]

Including the BlobContainer system program in a KasperskyOS-based solution

If a solution uses dynamic libraries, the system program BlobContainer must be included in this solution (this system program is provided by the executable file sysroot-aarch64-kos/bin/BlobContainer from KasperskyOS Community Edition).

The BlobContainer program can be included in a solution either automatically or manually. This program is automatically included in a solution by running the CMake commands build_kos_qemu_image() and build_kos_hw_image() if at least one executable file in the solution is linked to a dynamic library. (To disable automatic inclusion of the BlobContainer program in a solution, you need to add the NO_AUTO_BLOB_CONTAINER value to the parameters of the CMake commands build_kos_qemu_image() and build_kos_hw_image().) If programs in a solution work with dynamic libraries using only a POSIX interface (the dlopen(), dlsym(), dlerror(), and dlclose() functions), the BlobContainer program needs to be manually included in the solution.

The VFS working with files of dynamic libraries must be a separate process. You need to create an IPC channel from the process of the BlobContainer program to the VFS process.

You must create IPC channels from the processes using dynamic libraries to the process of the BlobContainer program. These IPC channels can be created statically or dynamically. If a statically created IPC channel is not available, the client and server parts of the BlobContainer program attempt to dynamically create an IPC channel using the name server.

If the BlobContainer program is automatically included in a solution, the @INIT_EXTERNAL_ENTITIES@, @INIT_<program name>_ENTITY_CONNECTIONS@ and @INIT_<program name>_ENTITY_CONNECTIONS+@ macros used in the init.yaml.in file automatically create within the init description dictionaries of IPC channels that enable static creation of IPC channels from processes created based on executable files linked to dynamic libraries to the process of the BlobContainer program. (The process of the BlobContainer program receives the name kl.bc.BlobContainer, while the IPC channels receive the name kl.BlobContainer.) However, dictionaries of IPC channels to the BlobContainer program process are not automatically created for processes that work with dynamic libraries using only a POSIX interface. To ensure that the required IPC channels are statically created, these dictionaries must be manually created (these IPC channels must have the name kl.BlobContainer).

If the BlobContainer program is manually included in the solution and you need to statically create IPC channels from processes using dynamic libraries to the BlobContainer program process, you must manually create dictionaries of the required IPC channels in the init description. By default, the IPC channel to the BlobContainer program process has the name kl.BlobContainer. However, this name can be changed through the environment variable _BLOB_CONTAINER_BACKEND. This variable must be defined for the BlobContainer process and for processes using dynamic libraries.

The environment variable _BLOB_CONTAINER_BACKEND defines not only the name of statically created IPC channels to the BlobContainer program process, but also defines the endpoint name that is published on the name server and used to dynamically create IPC channels to the BlobContainer program process. This is convenient when multiple processes of the BlobContainer program are running simultaneously (for example, to isolate its own dynamic libraries from external ones), and when different processes using dynamic libraries must interact over IPC with different processes of the BlobContainer program. In this case, you need to define different values for the environment variable _BLOB_CONTAINER_BACKEND for different processes of the BlobContainer program, and then use these values for the environment variable _BLOB_CONTAINER_BACKEND for processes using dynamic libraries. The specific value must be selected depending on the specific process of the BlobContainer program that requires the dynamically created IPC channel.

Example use of the environment variable _BLOB_CONTAINER_BACKEND in the init.yaml.in file:

entities: - name: example.BlobContainer path: example_blob_container args: - "-v" env: _BLOB_CONTAINER_BACKEND: kl.custombc @INIT_example_blob_container_ENTITY_CONNECTIONS@ - name: client.Client path: client env: _BLOB_CONTAINER_BACKEND: kl.custombc @INIT_client_ENTITY_CONNECTIONS@ @INIT_EXTERNAL_ENTITIES@

Example use of the environment variable _BLOB_CONTAINER_BACKEND in CMake commands:

set_target_properties (ExecMgrEntity PROPERTIES EXTRA_ENV " _BLOB_CONTAINER_BACKEND: kl.custombc") set_target_properties (dump_collector::entity PROPERTIES EXTRA_ENV " _BLOB_CONTAINER_BACKEND: kl.custombc")
Page top
[Topic shared_libraries_blobcontainer]

Building dynamic libraries

To build a dynamic library, you need to use the following CMake command:

add_library(<build target name> SHARED <path to a file of the library source code>…)

You can also build a dynamic library by using the following CMake command:

add_library(<build target name> <path to a file of the library source code>…)

The cmake shell command must be called with the -D BUILD_SHARED_LIBS=YES parameter. (If the cmake shell command is called without the -D BUILD_SHARED_LIBS=YES parameter, a static library will be built.)

Example:

#!/bin/bash … cmake -G "Unix Makefiles" \ -D CMAKE_BUILD_TYPE:STRING=Debug \ -D CMAKE_TOOLCHAIN_FILE=$SDK_PREFIX/toolchain/share/toolchain-$TARGET.cmake \ -D BUILD_SHARED_LIBS=YES \ -B build \ && cmake --build build --target kos-image

By default, the library file name matches the name of the build target defined via the parameter of the CMake add_library() command. The library file name can be changed by using the CMake set_target_properties() command. This can be done to make the library file name identical for its dynamic and static variants.

Example:

# Build the static library add_library(somelib_static STATIC src/somesrc.cpp) set_target_properties(somelib_static PROPERTIES OUTPUT_NAME "somelib") # Build the dynamic library add_library(somelib_shared SHARED src/somesrc.cpp) set_target_properties(somelib_shared PROPERTIES OUTPUT_NAME "somelib")

A dynamic library can be linked to other static and dynamic libraries by using the CMake target_link_libraries() command. In this case, static libraries must be built with the -fPIC flag. This flag is applied when building a static library if the following CMake command is used:

set_property(TARGET <build target name>… PROPERTY POSITION_INDEPENDENT_CODE ON)
Page top
[Topic shared_libraries_building]

Adding dynamic libraries to a KasperskyOS-based solution

There are two ways to add dynamic libraries to a KasperskyOS-based solution: by either automatic or manual search for the dynamic libraries required by the solution programs.

Automatic search for dynamic libraries

Use the PACK_DEPS_COPY_ONLY ON, PACK_DEPS_LIBS_PATH, and PACK_DEPS_COPY_TARGET parameters in the CMake commands build_kos_qemu_image() and build_kos_hw_image().

Dynamic libraries that are loaded into memory by calling the dlopen() function of the POSIX interface will not be found by an automatic search.

Example 1:

# This example creates a solution image for the hardware platform. # This solution image must include the storage drive image containing the dynamic # libraries. # Setting values of variables set (IMAGE_FS ${CMAKE_BINARY_DIR}/hdd) set (LIBS_PATH "${IMAGE_FS}/lib") set (DISK_IMG ramdisk0.img) if (blob_container_ENTITY_FOUND) # Configure the BlobContainer program set_target_properties (${blob_container_ENTITY} PROPERTIES DEPENDS_ON_ENTITY ${precompiled_vfsVfsRamFs} EXTRA_ENV " VFS_FILESYSTEM_BACKEND: client:kl.VfsRamFs") endif () # Create a storage drive image containing the dynamic libraries # The storage drive image will be created after completing the copylibs target, # which is created by the Cmake command build_kos_hw_image(). add_custom_command (OUTPUT ${DISK_IMG} DEPENDS copylibs COMMAND ${KL_SDK_ROOT_PATH}/common/prepare_hdd_img.sh -d ${IMAGE_FS} -s 64 -f ext4 -img ${DISK_IMG} COMMENT "Creating disk image '${DISK_IMG}' from files in '${IMAGE_FS}' ...") # Create a solution image for the hardware platform # The copylibs target indicates that the automatically found dynamic # libraries will be copied to the directory ${LIBS_PATH}. This directory will be included # in the storage drive image. The value ${DISK_IMG} is specified in the # IMAGE_FILES parameter, so the storage drive image will be created before the solution image is created, # and it will be included in the solution image. build_kos_hw_image (kos-image ... IMAGE_FILES ${ENTITIES_LIST} ${DISK_IMG} PACK_DEPS_COPY_ONLY ON PACK_DEPS_LIBS_PATH ${LIBS_PATH} PACK_DEPS_COPY_TARGET copylibs)

Example 2:

# This example to be run in QEMU creates a solution image and # a separate storage drive image containing the dynamic libraries, # and this example to be run on the hardware platform creates an SD card image # to which the solution image and dynamic libraries are added. # Set the values of variables used during the build # for the hardware platform set (HW_IMAGE_FS "${CMAKE_BINARY_DIR}/hdd_hw") set (HW_LIBS_PATH "${HW_IMAGE_FS}/lib") # Set the values of variables used during the build # for QEMU set (QEMU_IMAGE_FS "${CMAKE_BINARY_DIR}/hdd_qemu") set (QEMU_LIBS_PATH "${QEMU_IMAGE_FS}/lib") set (QEMU_DISK_IMAGE sdcard0.img) set (QEMU_FLAGS "-nic none -m 2048 -drive file=${QEMU_DISK_IMAGE},if=sd,format=raw") set (QEMU_DEPENDENCIES ${QEMU_DISK_IMAGE}) if (blob_container_ENTITY_FOUND) # Configure the BlobContainer program set_target_properties (${blob_container_ENTITY} PROPERTIES DEPENDS_ON_ENTITY ${precompiled_vfsVfsSdCardFs} EXTRA_ENV " VFS_FILESYSTEM_BACKEND: client:kl.VfsSdCardFs") # Create a storage drive image containing the dynamic libraries for QEMU add_custom_command (${QEMU_DISK_IMAGE} DEPENDS copylibs_qemu COMMAND ${KL_SDK_ROOT_PATH}/common/prepare_hdd_img.sh -d ${QEMU_IMAGE_FS} -s 64 -f fat32 -img ${QEMU_DISK_IMAGE} COMMENT "Creating disk image '${QEMU_DISK_IMAGE}' from files in '${QEMU_IMAGE_FS}' ...") # Create a solution image for QEMU # The copylibs_qemu target indicates that the automatically found dynamic # libraries will be copied to the directory ${QEMU_LIBS_PATH}. This directory will be included # in the storage drive image containing the dynamic libraries for QEMU. The value ${QEMU_FLAGS} # contains the -drive file=${QEMU_DISK_IMAGE},if=sd,format=raw parameters, which # are required for QEMU use of the storage drive image containing the dynamic # libraries. The value ${QEMU_DEPENDENCIES} is required to make sure that # the storage drive image containing the dynamic libraries is created before creation of the # solution image for QEMU. (The solution image will not include the storage drive image containing the # dynamic libraries, but you must add this target dependency # to create the storage drive image containing the dynamic libraries.) build_kos_qemu_image (kos-qemu-image ... QEMU_FLAGS "${QEMU_FLAGS}" QEMU_DEPENDENCIES "${QEMU_DEPENDENCIES}" PACK_DEPS_COPY_ONLY ON PACK_DEPS_LIBS_PATH "${QEMU_LIBS_PATH}" PACK_DEPS_COPY_TARGET copylibs_qemu IMAGE_FILES ${ENTITIES}) # Create a solution image for the hardware platform # The copylibs_hw target indicates that the automatically found dynamic # libraries will be copied to the directory ${HW_LIBS_PATH}. This directory will be included # in the SD card image that is created by the CMake command build_sd_image(). build_kos_hw_image (kos-image ... PACK_DEPS_COPY_ONLY ON PACK_DEPS_LIBS_PATH "${HW_LIBS_PATH}" PACK_DEPS_COPY_TARGET copylibs_hw IMAGE_FILES ${ENTITIES}) # The solution image for the hardware platform will be created after # the automatically found dynamic libraries are copied to the directory ${HW_LIBS_PATH}. # (The solution image will not include dynamic libraries, but you must add # this target dependency to complete copying prior to creation of # the SD card image.) add_dependencies (kos-image copylibs_hw) # Create the SD card image # The SD card image will be created based on the directory ${HW_IMAGE_FS}, # which includes the solution for the hardware platform and the directory # containing the dynamic libraries. The SD card image will be created # after creation of the solution image for the hardware platform. build_sd_image (sd-image KOS_IMAGE_TARGET kos-image IMAGE_FS ${HW_IMAGE_FS})

Manual search for dynamic libraries

You need to independently search for dynamic libraries required by the solution programs. This way, even dynamic libraries that were loaded into memory by calling the dlopen() function of the POSIX interface can be added to a solution.

Example 1:

# This example creates a solution image that includes # the libm.so dynamic library in addition to the executable files of programs. # Configure the VFS program set (VFS_SDCARD_ARGS "\ - -l - nodev /tmp ramfs 0 - -l - devfs /dev devfs 0 - -l - romfs /lib romfs ro") set_target_properties (${precompiled_vfsVfsSdCardFs} PROPERTIES EXTRA_ARGS ${VFS_SDCARD_ARGS}) if (blob_container_ENTITY_FOUND) # Configure the BlobContainer program set_target_properties (${blob_container_ENTITY} PROPERTIES DEPENDS_ON_ENTITY ${precompiled_vfsVfsSdCardFs} EXTRA_ENV " VFS_FILESYSTEM_BACKEND: client:kl.VfsSdCardFs") endif () if(PLATFORM_SUPPORTS_DYNAMIC_LINKING) # Get the full path to the libm.so dynamic library find_file(LIBM_SO_FILE libm.so PATH_SUFFIXES lib REQUIRED) set(EXTRA_FILES ${LIBM_SO_FILE}) else() set(EXTRA_FILES) endif() # Create a solution image for the hardware platform # The libm.so dynamic library will be added to the # solution image because the value ${EXTRA_FILES} # specified in the IMAGE_FILES parameter contains the full # path to this library. build_kos_hw_image(kos-image ... IMAGE_FILES ${ENTITIES} ${EXTRA_FILES})

Example 2:

# This example to be run in QEMU creates a solution image and # a separate storage drive image containing the libm.so dynamic library. # Setting values of variables set (QEMU_FLAGS "-nic none -m 2048") set (QEMU_DEPENDENCIES) if (PLATFORM_SUPPORTS_DYNAMIC_LINKING) # Setting values of variables set (IMAGE_FS "${CMAKE_BINARY_DIR}/hdd") set (LIBS_PATH "${IMAGE_FS}/lib") set (QEMU_DISK_IMAGE sdcard0.img) string (APPEND QEMU_FLAGS "-drive file=${QEMU_DISK_IMAGE},if=sd,format=raw") set (QEMU_DEPENDENCIES ${QEMU_DISK_IMAGE}) # Configure the BlobContainer program set_target_properties (${blob_container_ENTITY} PROPERTIES DEPENDS_ON_ENTITY ${precompiled_vfsVfsSdCardFs} EXTRA_ENV " VFS_FILESYSTEM_BACKEND: client:kl.VfsSdCardFs") # Get the full path to the libm.so dynamic library find_file (LIBM_SO_FILE libm.so PATH_SUFFIXES lib REQUIRED) # Create a storage drive image containing the libm.so dynamic library add_custom_command (OUTPUT ${QEMU_DISK_IMAGE} COMMAND ${CMAKE_COMMAND} -E make_directory ${LIBS_PATH} COMMAND ${CMAKE_COMMAND} -E copy ${LIBM_SO_FILE} ${LIBS_PATH} COMMAND ${KL_SDK_ROOT_PATH}/common/prepare_hdd_img.sh -d ${IMAGE_FS} -s 64 -f fat32 -img ${QEMU_DISK_IMAGE} COMMENT "Creating disk image '${QEMU_DISK_IMAGE}' from files in '${IMAGE_FS}' ...") endif () # Create a solution image for QEMU # The value ${QEMU_FLAGS} contains the -drive file=${QEMU_DISK_IMAGE},if=sd,format=raw parameters, # which are required for QEMU use of the storage drive image containing the # libm.so dynamic library. The value ${QEMU_DEPENDENCIES} is required to make sure that # the storage drive image containing the libm.so dynamic library is created before creation of the # solution image for QEMU. (The solution image will not include the storage drive image containing the # libm.so dynamic library, but you must add this target dependency # to create the storage drive image containing the libm.so dynamic library.) build_kos_qemu_image (kos-qemu-image ... QEMU_FLAGS "${QEMU_FLAGS}" QEMU_DEPENDENCIES "${QEMU_DEPENDENCIES}" IMAGE_FILES ${ENTITIES})
Page top
[Topic shared_libraries_adding_to_solution]