KasperskyOS Community Edition 1.2

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, a program 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 programs 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 a program 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 the KasperskyOS SDK, 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 image

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 may be implemented in the context of the processes using the dynamic libraries, or may be 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 toolchain must support dynamic linking.

    The KasperskyOS SDK comes with a separate toolchain for each supported processor architecture. A required toolchain may not support dynamic linking. To check whether dynamic linking is supported, you need to use the CMake get_property() command in the CMakeLists.txt root file as follows:

    get_property(CAN_SHARED GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS) if(CAN_SHARED) message(STATUS "Dynamic linking is supported.") endif()
  3. The executable code of programs that use dynamic libraries must be built with the -rdynamic flag (with dynamic linking).

    If the toolchain supports 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 initialize_platform(FORCE_STATIC) command is called in the CMakeLists.txt root file, the toolchain supporting dynamic linking 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 that supports dynamic linking 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 that supports dynamic linking performs static linking of this executable file.

The executable code of programs 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 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 a program is loaded into memory upon startup of the process in whose context this program is executed. A running process can load a dynamic library into memory by calling the dlopen() function of the POSIX interface. A dynamic library may be linked to other dynamic libraries, so a program 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 memory together with all of the dynamic libraries that it depends on.

    If the BlobContainer system program is included in a KasperskyOS-based solution, 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.) If the BlobContainer system program is not included in a solution, separate instances of a dynamic library are loaded into the memory of processes that are using this library. A dynamic library on which several other dynamic libraries depend is loaded into shared memory or into the memory of a process 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 memory even if the program 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 loaded into shared memory or process memory. This can be used for debugging purposes if you need to replace functions imported from dynamic libraries.

    The dynamic library loader searches for program-dependent 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 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 that was loaded into process memory by calling the dlopen() function is unloaded by calling the dlclose() function. A dynamic library that is linked to a program cannot be unloaded from memory until termination of the process in whose context this program is executed. A dynamic library that is linked to other dynamic libraries is unloaded from memory after all libraries that depend on it are unloaded, or after the process is terminated.

Page top
[Topic shared_libraries_lifecycle]

Including the BlobContainer system program in a KasperskyOS-based solution

If the BlobContainer program is provided in the KasperskyOS SDK, it must be included into a solution in which dynamic libraries are used. To check whether the BlobContainer program is included in the KasperskyOS SDK, you need to make sure that the sysroot-*-kos/bin/BlobContainer executable file is available.

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 program 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.

When using the BlobContainer program, 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 between processes of programs linked to dynamic libraries and 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")

If the BlobContainer program is being used, the VFS working with files of dynamic libraries must be a separate process. An IPC channel must also be created from the process of the BlobContainer program to the VFS process.

Page top
[Topic shared_libraries_blobcontainer]

Building dynamic libraries

When building dynamic libraries, you must use a toolchain that supports dynamic linking.

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

add_library(<build target name> SHARED [list of paths to files of the library source code])

Use of this CMake command results in an error if the toolchain does not support dynamic linking.

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

add_library(<build target name> [list of paths to files 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") # The PLATFORM_SUPPORTS_DYNAMIC_LINKING variable has the # value "true" when using dynamic # linking. If initialize_platform(FORCE_STATIC) is called, # this variable has the value "false". if(PLATFORM_SUPPORTS_DYNAMIC_LINKING) # Build the dynamic library add_library(somelib_shared SHARED src/somesrc.cpp) set_target_properties(somelib_shared PROPERTIES OUTPUT_NAME "somelib") endif()

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 <list of names of build targets> PROPERTY POSITION_INDEPENDENT_CODE ON)
Page top
[Topic shared_libraries_building]

Adding dynamic libraries to a KasperskyOS-based solution image

To add dynamic libraries to the KasperskyOS-based solution image, use 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().

Example:

set(RESOURCES ${CMAKE_SOURCE_DIR}/resources) set(FSTAB ${RESOURCES}/fstab) set(DISK_IMG ${CMAKE_CURRENT_BINARY_DIR}/ramdisk0.img) set(RESOURCES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../resources) set(EXT4_PART_DIR ${CMAKE_CURRENT_BINARY_DIR}/../system_hdd) set_target_properties(${vfs_ENTITY} PROPERTIES EXTRA_ARGS " - \"-f\" - \"fstab\"" EXTRA_ENV " ROOTFS: ramdisk0 / ext4 0" ${blkdev_ENTITY}_REPLACEMENT "${ramdisk_ENTITY};${sdcard_ENTITY}") add_custom_target(copy-so) add_custom_command(OUTPUT ${DISK_IMG} COMMAND ${CMAKE_COMMAND} -E copy_directory ${RESOURCES_DIR}/rootdir ${EXT4_PART_DIR} COMMAND mke2fs -v -d ${EXT4_PART_DIR} -t ext4 ${DISK_IMG} 40M DEPENDS copy-so COMMENT "Creating disk image '${DISK_IMG}' from files in '${EXT4_PART_DIR}' ...") build_kos_hw_image(kos-image ... IMAGE_FILES ${ENTITIES_LIST} ${FSTAB} ${DISK_IMG} PACK_DEPS_COPY_ONLY ON PACK_DEPS_LIBS_PATH ${EXT4_PART_DIR}/lib PACK_DEPS_COPY_TARGET copylibs) if(PLATFORM_SUPPORTS_DYNAMIC_LINKING) add_dependencies(copy-so copylibs) endif()

The solution program-dependent dynamic libraries are added to a storage device image (for example, one with an ext4 file system) that will be included into the solution image.

Dynamic libraries that are loaded into memory by calling the dlopen() function of the POSIX interface are not added to the solution image.

The build system does the following:

  • Searches for dynamic libraries and copies these libraries to the directory whose path is specified in the PACK_DEPS_LIBS_PATH parameter of the CMake commands build_kos_qemu_image() and build_kos_hw_image(). (To ensure that the found dynamic libraries are included in the storage device image, this directory must reside in the file system that will be put into the storage device image.)
  • Creates a storage device image that includes the directory containing the dynamic libraries.

    To create a storage device image, use the CMake command add_custom_command(). The target specified in the DEPENDS parameter of the CMake command add_custom_command(), indicates that a storage device image is created. The target specified in the PACK_DEPS_COPY_TARGET parameter of the CMake commands build_kos_qemu_image() and build_kos_hw_image(), indicates that dynamic libraries are copied. To make sure that the storage device image is created only after the dynamic libraries are fully copied, use the CMake command add_dependencies().

  • Adds the storage device image to the solution image.

    To add the storage device image to the solution image, specify the full path to the storage device image in the IMAGE_FILES parameter of the CMake commands build_kos_qemu_image() and build_kos_hw_image().

Page top
[Topic shared_libraries_adding_to_image]