KasperskyOS Community Edition 1.2

Использование динамических библиотек

В решении на базе KasperskyOS можно использовать динамические библиотеки (файлы *.so). По сравнению со статическими библиотеками (файлами *.a) динамические библиотеки дают следующие преимущества:

  • Экономия оперативной памяти.

    Несколько процессов могут использовать один экземпляр динамической библиотеки. Также программа и динамические библиотеки в одном процессе могут использовать один экземпляр динамической библиотеки.

    Динамические библиотеки могут загружаться в память и выгружаться из нее по инициативе программ, которые их используют.

  • Удобство обновления ПО.

    Обновление динамической библиотеки распространяется на все зависимые от нее программы и динамические библиотеки без их повторной сборки.

  • Возможность реализовать механизм плагинов.

    Плагины для компонентов решения представляют собой динамические библиотеки.

  • Совместное использование кода и данных.

    Один экземпляр динамической библиотеки может совместно использоваться несколькими процессами, а также программой и динамическими библиотеками в одном процессе. Это позволяет, например, централизованно управлять множественным доступом к ресурсам или хранить общие данные.

Динамические библиотеки поставляются в составе KasperskyOS SDK, а также могут быть созданы разработчиком решения на базе KasperskyOS. Работоспособность сторонних динамических библиотек не гарантируется.

В настоящее время из-за технических ограничений в решении на базе KasperskyOS нельзя использовать libc.so и libpthread.so.

В этом разделе

Условия, необходимые для использования динамических библиотек

Жизненный цикл динамической библиотеки

Включение системной программы BlobContainer в решение на базе KasperskyOS

Сборка динамических библиотек

Добавление динамических библиотек в образ решения на базе KasperskyOS

В начало
[Topic shared_libraries]

Условия, необходимые для использования динамических библиотек

Чтобы использовать динамические библиотеки в решении на базе KasperskyOS, нужно выполнить следующие условия:

  1. Процессы, использующие динамические библиотеки, должны иметь доступ к файловым системам, в которых хранятся файлы динамических библиотек. Доступ к файловым системам обеспечивается VFS, которая может исполняться в контексте процессов, использующих динамические библиотеки, а также может быть отдельным процессом. VFS и другое ПО, с помощью которого VFS работает с накопителем (например драйвер накопителя), не должны использовать динамические библиотеки.
  2. Тулчейн должен поддерживать динамическую компоновку.

    В составе KasperskyOS SDK поставляется отдельный тулчейн для каждой поддерживаемой архитектуры процессора. Требуемый тулчейн может не поддерживать динамическую компоновку. Чтобы проверить, что динамическая компоновка поддерживается, нужно использовать в корневом файле CMakeLists.txt CMake-команду get_property() следующим образом:

    get_property(CAN_SHARED GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS) if(CAN_SHARED) message(STATUS "Dynamic linking is supported.") endif()
  3. Исполняемый код программ, использующих динамические библиотеки, должен быть собран с флагом -rdynamic (с динамической компоновкой).

    Если тулчейн поддерживает динамическую компоновку, то CMake-команда initialize_platform() делает так, что для сборки всех исполняемых файлов, заданных через CMake-команды add_executable(), этот флаг используется автоматически.

Если CMake-команда initialize_platform(FORCE_STATIC) вызывается в корневом файле CMakeLists.txt, то тулчейн, поддерживающий динамическую компоновку, выполняет статическую компоновку исполняемых файлов.

CMake-команда project_static_executable_header_default() влияет на сборку исполняемых файлов, заданных через последующие CMake-команды add_executable() в одном файле CMakeLists.txt. Тулчейн, поддерживающий динамическую компоновку, выполняет статическую компоновку этих исполняемых файлов.

CMake-команда platform_target_force_static() влияет на сборку одного исполняемого файла, заданного через CMake-команду add_executable(). Тулчейн, поддерживающий динамическую компоновку, выполняет статическую компоновку этого исполняемого файла.

Исполняемый код программ, который собирается с флагом -rdynamic, компонуется со статической библиотекой, если динамическая библиотека не найдена. Например, если используется CMake-команда target_link_libraries(client -lm), программа client компонуется со статической библиотекой libm.a, если динамическая библиотека libm.so не найдена.

В начало
[Topic shared_libraries_use_conditions]

Жизненный цикл динамической библиотеки

Жизненный цикл динамической библиотеки включает следующие стадии:

  1. Загрузка в память.

    Динамическая библиотека, скомпонованная с программой, загружается в память при запуске процесса, в контексте которого эта программа исполняется. Запущенный процесс может загрузить динамическую библиотеку в память вызовом функции dlopen() интерфейса POSIX. Динамическая библиотека может быть скомпонована с другими динамическими библиотеками, поэтому программа зависит не только от непосредственно скомпонованной с ней динамической библиотеки, но и от всего графа зависимостей этой библиотеки. Динамическая библиотека загружается в память совместно с всеми динамическими библиотеками, от которых зависит.

    Если системная программа BlobContainer включена в решение на базе KasperskyOS, то один экземпляр динамической библиотеки загружается в разделяемую память независимо от того, сколько процессов использует эту библиотеку. (Точнее, в разделяемую память загружается только часть динамической библиотеки, включающая код и доступные только на чтение данные. Другая часть динамической библиотеки в любом случае загружается в память каждого процесса, который использует эту библиотеку.) Если системная программа BlobContainer не включена в решение, то отдельные экземпляры динамической библиотеки загружаются в память процессов, использующих эту библиотеку. Динамическая библиотека, от которой зависит нескольких других динамических библиотек, загружается в разделяемую память или память процесса в единственном экземпляре.

    Если задать через переменную окружения LD_PRELOAD список динамических библиотек, то эти динамические библиотеки будут загружены в память, даже если программа не зависит от них. (Элементами списка должны быть абсолютные или относительные пути к динамическим библиотекам, разделенные двоеточием, например: LD_PRELOAD=libmalloc.so:libfree.so:/usr/somepath/lib/libfoo.so.) Функции, которые экспортируются динамическими библиотеками, указанными в LD_PRELOAD, замещают одноименные функции, которые экспортируются другими загруженными в разделяемую память или память процесса динамическими библиотеками. Это можно использовать для целей отладки, если требуется подменить функции, импортируемые из динамических библиотек.

    Загрузчик динамических библиотек выполняет поиск динамических библиотек, от которых зависят программы, в следующем порядке:

    1. По абсолютным путям, заданным через переменную окружения LD_LIBRARY_PATH.

      Пути должны быть разделены двоеточием, например: LD_LIBRARY_PATH=/usr/lib:/home/user/lib.

    2. По абсолютным путям, заданным в поле DT_RUNPATH или DT_RPATH секции .dynamic исполняемых файлов и динамических библиотек.

      При компоновке исполняемых файлов и динамических библиотек могут быть заданы пути, по которым загрузчик динамических библиотек будет выполнять поиск. (Это можно сделать, например, через свойство INSTALL_RPATH в CMake-команде set_target_properties().) Пути для поиска динамических библиотек сохраняются в поле DT_RUNPATH или DT_RPATH секции .dynamic. Это поле может быть как в исполняемых файлах, скомпонованных с динамическими библиотеки, так и в самих динамических библиотеках, скомпонованных с другими динамическими библиотеками.

    3. По пути /lib.

    Загрузчик динамических библиотек выполняет поиск в том же порядке, если в параметре filename функции dlopen() или в переменной окружения LD_PRELOAD указан относительный путь к динамической библиотеке. Если указан абсолютный путь, то загрузчик помещает динамическую библиотеку в память без выполнения поиска.

  2. Использование процессом (процессами).
  3. Выгрузка из памяти.

    Динамическая библиотека выгружается из разделяемой памяти, когда все процессы, использующие эту библиотеку, завершились или вызвали функцию dlclose() интерфейса POSIX. Динамическая библиотека, загруженная в память процесса вызовом функции dlopen(), выгружается вызовом функции dlclose(). Динамическая библиотека, скомпонованная с программой, не может быть выгружена из памяти до завершения процесса, в контексте которого эта программа исполняется. Динамическая библиотека, скомпонованная с другими динамическими библиотеками, выгружается из памяти после выгрузки всех библиотек, которые зависят от нее, либо после завершения процесса.

В начало
[Topic shared_libraries_lifecycle]

Включение системной программы BlobContainer в решение на базе KasperskyOS

Если программа BlobContainer поставляется в составе KasperskyOS SDK, ее необходимо включить в решение, в котором используются динамические библиотеки. Чтобы проверить, поставляется ли программа BlobContainer в составе KasperskyOS SDK, нужно убедиться в наличии исполняемого файла sysroot-*-kos/bin/BlobContainer.

Программа BlobContainer может быть включена в решение автоматически или вручную. Автоматическое включение этой программы в решение выполняется CMake-командами build_kos_qemu_image() и build_kos_hw_image(), если как минимум одна программа в решении скомпонована с динамической библиотекой. (Чтобы отключить автоматическое включение программы BlobContainer в решение, нужно добавить значение NO_AUTO_BLOB_CONTAINER в параметры CMake-команд build_kos_qemu_image() и build_kos_hw_image().) Если программы в решении работают с динамическими библиотеками, используя только интерфейс POSIX (функции dlopen(), dlsym(), dlerror(), dlclose()), то программу BlobContainer нужно включить в решение вручную.

В случае применения программы BlobContainer должны быть созданы IPC-каналы от процессов, использующих динамические библиотеки, к процессу программы BlobContainer. Эти IPC-каналы могут быть созданы как статически, так и динамически. Если статически созданный IPC-канал отсутствует, клиентская и серверная части программы BlobContainer пытаются создать IPC-канал динамически, используя сервер имен.

Если программа BlobContainer включена в решение автоматически, то макросы @INIT_EXTERNAL_ENTITIES@, @INIT_<имя программы>_ENTITY_CONNECTIONS@ и @INIT_<имя программы>_ENTITY_CONNECTIONS+@, используемые в файле init.yaml.in, автоматически создают в init-описании словари IPC-каналов, которые обеспечивают статическое создание IPC-каналов от процессов программ, скомпонованных с динамическими библиотеками, к процессу программы BlobContainer. (Процесс программы BlobContainer получает имя kl.bc.BlobContainer, а IPC-каналы получают имя kl.BlobContainer.) При этом для процессов, которые работают с динамическими библиотеками, используя только интерфейс POSIX, словари IPC-каналов к процессу программы BlobContainer автоматически не создаются, и, чтобы требуемые IPC-каналы были созданы статически, нужно создать эти словари вручную (эти IPC-каналы должны иметь имя kl.BlobContainer).

Если программа BlobContainer включена в решение вручную, и требуется статически создать IPC-каналы от процессов, использующих динамические библиотеки, к процессу программы BlobContainer, то нужно вручную создать словари необходимых IPC-каналов в init-описании. По умолчанию IPC-канал к процессу программы BlobContainer имеет имя kl.BlobContainer, но это имя можно изменить через переменную окружения _BLOB_CONTAINER_BACKEND. Эту переменную нужно задать как для процесса BlobContainer, так и для процессов, использующих динамические библиотеки.

Переменная окружения _BLOB_CONTAINER_BACKEND задает не только имя статически создаваемых IPC-каналов к процессу программы BlobContainer, но и имя службы, публикуемое на сервере имен, которое используется для динамического создания IPC-каналов к процессу программы BlobContainer. Это удобно использовать, когда запущено одновременно несколько процессов программы BlobContainer (например, с целью изоляции собственных динамических библиотек от сторонних), и разные процессы, использующие динамические библиотеки, должны взаимодействовать через IPC с разными процессами программы BlobContainer. В таком случае для разных процессов программы BlobContainer нужно задать разные значения переменной окружения _BLOB_CONTAINER_BACKEND, а затем использовать эти значения для переменной окружения _BLOB_CONTAINER_BACKEND процессов, использующих динамические библиотеки, выбирая конкретное значение в зависимости того, с каким именно процессом программы BlobContainer требуется динамически создать IPC-канал.

Пример использования переменной окружения _BLOB_CONTAINER_BACKEND в файле init.yaml.in:

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@

Пример использования переменной окружения _BLOB_CONTAINER_BACKEND в CMake-командах:

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")

Если используется программа BlobContainer, то VFS, работающая с файлами динамических библиотек, должна быть отдельным процессом. Также должен быть создан IPC-канал от процесса программы BlobContainer к процессу VFS.

В начало
[Topic shared_libraries_blobcontainer]

Сборка динамических библиотек

Для сборки динамических библиотек требуется использовать тулчейн, который поддерживает динамическую компоновку.

Чтобы выполнить сборку динамической библиотеки, нужно использовать следующую CMake-команду:

add_library(<имя цели сборки> SHARED [список путей к файлам исходного кода библиотеки])

При использовании этой CMake-команды возникает ошибка, если тулчейн не поддерживает динамическую компоновку.

Также можно выполнить сборку динамической библиотеки следующей CMake-командой:

add_library(<имя цели сборки> [список путей к файлам исходного кода библиотеки])

При этом shell-команду cmake нужно вызвать с параметром -D BUILD_SHARED_LIBS=YES. (Если вызвать shell-команду cmake без параметра -D BUILD_SHARED_LIBS=YES, будет выполнена сборка статической библиотеки.)

Пример:

#!/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

По умолчанию имя файла библиотеки совпадает с именем цели сборки, заданным через параметр CMake-команды add_library(). Имя файла библиотеки можно изменить, используя CMake-команду set_target_properties(). Это можно использовать, чтобы имя файла библиотеки было одинаковым для ее динамического и статического варианта.

Пример:

# Сборка статической библиотеки add_library(somelib_static STATIC src/somesrc.cpp) set_target_properties(somelib_static PROPERTIES OUTPUT_NAME "somelib") # Переменная PLATFORM_SUPPORTS_DYNAMIC_LINKING имеет # значение "истина", если используется динамическая # компоновка. Если вызывается initialize_platform(FORCE_STATIC), # эта переменная имеет значение "ложь". if(PLATFORM_SUPPORTS_DYNAMIC_LINKING) # Сборка динамической библиотеки add_library(somelib_shared SHARED src/somesrc.cpp) set_target_properties(somelib_shared PROPERTIES OUTPUT_NAME "somelib") endif()

Динамическая библиотека может быть скомпонована с другими статическими и динамическими библиотеками CMake-командой target_link_libraries(). При этом статические библиотеки должны быть собраны с флагом -fPIC. Этот флаг применяется при сборке статической библиотеки, если используется следующая CMake-команда:

set_property(TARGET <список имен целей сборки> PROPERTY POSITION_INDEPENDENT_CODE ON)
В начало
[Topic shared_libraries_building]

Добавление динамических библиотек в образ решения на базе KasperskyOS

Чтобы добавить динамические библиотеки в образ решения на базе KasperskyOS, нужно использовать параметры PACK_DEPS_COPY_ONLY ON, PACK_DEPS_LIBS_PATH и PACK_DEPS_COPY_TARGET в CMake-командах build_kos_qemu_image() и build_kos_hw_image().

Пример:

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()

Динамические библиотеки, от которых зависят программы решения, будут добавлены в образ накопителя (например, с файловой системой ext4), который будет включен в образ решения.

Динамические библиотеки, которые загружаются в память вызовом функции dlopen() интерфейса POSIX, не попадут в образ решения.

Система сборки выполняет следующие действия:

  • Осуществляет поиск динамических библиотек и копирует эти библиотеки в директорию, путь к которой указан в параметре PACK_DEPS_LIBS_PATH CMake-команд build_kos_qemu_image() и build_kos_hw_image(). (Чтобы найденные динамические библиотеки попали в образ накопителя, эта директория должна находиться в файловой системе, которая будет помещена в образ накопителя.)
  • Создает образ накопителя, который включает директорию с динамическими библиотеками.

    Чтобы создать образ накопителя, нужно использовать CMake-команду add_custom_command(). Цель, указанная в параметре DEPENDS CMake-команды add_custom_command(), означает создание образа накопителя. Цель, указанная в параметре PACK_DEPS_COPY_TARGET CMake-команд build_kos_qemu_image() и build_kos_hw_image(), означает копирование динамических библиотек. Чтобы образ накопителя был создан только после завершения копирования динамических библиотек, нужно использовать CMake-команду add_dependencies().

  • Добавляет образ накопителя в образ решения.

    Чтобы добавить образ накопителя в образ решения, нужно указать полный путь к образу накопителя в параметре IMAGE_FILES CMake-команд build_kos_qemu_image() и build_kos_hw_image().

В начало
[Topic shared_libraries_adding_to_image]