Contents
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.
Prerequisites for using dynamic libraries
To use dynamic libraries in a KasperskyOS-based solution, the following conditions must be met:
- 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).
- 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 theCMakeLists.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() - 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 viaCMake
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.
Life cycle of a dynamic library
The life cycle of a dynamic library includes the following phases:
- 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 theBlobContainer
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 inLD_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:
- 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
. - Absolute paths defined in the
DT_RUNPATH
orDT_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 theCMake
commandset_target_properties()
.) Paths used to search for dynamic libraries are stored in theDT_RUNPATH
orDT_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. - 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 thedlopen()
function or in theLD_PRELOAD
environment variable. If the absolute path is specified, the loader puts the dynamic library into memory without performing a search. - Absolute paths defined through the
- Use by a process (or processes).
- 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 thedlopen()
function is unloaded by calling thedlclose()
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.
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:
Example use of the environment variable _BLOB_CONTAINER_BACKEND
in CMake
commands:
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.
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:
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:
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:
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:
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:
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:
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 theCMake
commandsbuild_kos_qemu_image()
andbuild_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
commandadd_custom_command()
. The target specified in theDEPENDS
parameter of theCMake
commandadd_custom_command()
, indicates that a storage device image is created. The target specified in thePACK_DEPS_COPY_TARGET
parameter of theCMake
commandsbuild_kos_qemu_image()
andbuild_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 theCMake
commandadd_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 theCMake
commandsbuild_kos_qemu_image()
andbuild_kos_hw_image()
.