KasperskyOS Community Edition 1.3

Отладка программ в решении на базе KasperskyOS

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

Чтобы выполнить отладку программ в решении на базе KasperskyOS, нужно использовать отладчик GDB из состава KasperskyOS Community Edition на компьютере под управлением ОС Ubuntu. Отладку можно выполнить как на QEMU из состава KasperskyOS Community Edition, так и на аппаратной платформе. Для отладки на QEMU нужно использовать GDB-сервер QEMU или GDB-сервер ядра KasperskyOS. Для отладки на аппаратной платформе требуется использовать GDB-сервер ядра KasperskyOS.

Работоспособность отладчика GDB и QEMU не из состава KasperskyOS Community Edition не гарантируется.

Отладчик GDB по умолчанию поддерживает OS ABI (англ. Operating System ABI), специфичный для KasperskyOS. При изменении профиля OS ABI, заданного по умолчанию, возможность полноценной отладки не гарантируется.

Можно выполнять отладку одновременно нескольких программ решения, загрузив отладочные символы для нескольких исполняемых файлов.

Отладочные символы исполняемых файлов могут сохраняться в самих исполняемых файлах либо в отдельных файлах. Во втором случае можно существенно уменьшить размер образа решения. Чтобы сохранить отладочные символы в файлах *.dbg, нужно использовать следующую CMake-команду:

add_custom_command(TARGET <имя программы> POST_BUILD COMMAND ${CMAKE_OBJCOPY} --only-keep-debug $<TARGET_FILE:<имя программы>> $<TARGET_FILE_NAME:<имя программы>>.dbg COMMAND ${CMAKE_OBJCOPY} --strip-debug $<TARGET_FILE:<имя программы>> COMMAND ${CMAKE_OBJCOPY} --add-gnu-debuglink=$<TARGET_FILE_NAME:<имя программы>>.dbg $<TARGET_FILE:<имя программы>>)

Обратная трассировка стека при аварийном завершении процесса

Если процесс завершается аварийно, то ядро KasperskyOS выводит данные обратной трассировки стека (сведения о стеке вызовов) для потока исполнения, в котором возникло необработанное исключение. Эти данные позволяют определить последовательность вызовов функций, которая привела к аварийному завершению процесса.

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

Поддержка санитайзеров

В KasperskyOS Community Edition поддерживаются санитайзеры ASAN и UBSAN.

В этом разделе справки

Отладка программ с использованием GDB-сервера QEMU

Отладка программ с использованием GDB-сервера ядра KasperskyOS

Анализ данных обратной трассировки стека при аварийном завершении процесса

В начало
[Topic debugging]

Отладка программ с использованием GDB-сервера QEMU

GDB-сервер QEMU имеет следующие особенности:

  • Не распознает процессы и потоки исполнения, управляемые KasperskyOS.

    Все процессы, управляемые KasperskyOS, рассматриваются GDB-сервером QEMU как один процесс. Этот процесс включает один или несколько потоков исполнения в зависимости от того, сколько потоков может исполняться параллельно на аппаратной платформе, которую эмулирует QEMU. Таким образом, процессы и потоки исполнения, с которыми работает GDB-сервер QEMU, не являются процессами и потоками исполнения, управляемыми KasperskyOS.

  • Не гарантирует возможности отладки при эмуляции на QEMU аппаратных платформ с симметричной многопроцессорностью (англ. Symmetric Multiprocessing, SMP).

    GDB-сервер QEMU можно использовать при эмуляции на QEMU аппаратных платформ с SMP. Но при этом, например, могут не работать GDB-команды исполнения программы по шагам (например, next, step).

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

Подготовка к отладке

Начальные шаги отладки

Особенности отладки при эмуляции аппаратных платформ с SMP

В начало
[Topic debugging_qemustub]

Подготовка к отладке

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

  1. Отключить поддержку ASLR и задать адреса памяти для загрузки секций .text исполняемых файлов.

    Для этого нужно использовать следующую CMake-команду:

    set_target_properties(<имя программы> PROPERTIES LINK_FLAGS "-no-pie -Ttext <адрес>")

    Отладчику GDB нужен адрес памяти, по которому загружен ELF-образ, чтобы сопоставить отладочные символы с ELF-образом. ASLR, поддерживаемая по умолчанию, приводит к тому, что этот адрес задается случайным значением, которое не известно отладчику GDB. Это затрудняет отладку. Чтобы отключить поддержку ASLR, нужно указать флаг -no-pie.

    GDB-сервер QEMU рассматривает все исполняющиеся процессы, управляемые KasperskyOS, как один процесс. Если ASLR не поддерживается, то по умолчанию компоновщик делает так, что исполняемые файлы загружаются по одному и тому же адресу памяти процессов. В результате этого загружаемые сегменты исполняемых файлов пересекаются в памяти с точки зрения отладчика GDB, и отладка выполняется некорректно (например, отображаются некорректные значения переменных, или срабатывают точки останова, установленные для другой программы). Чтобы избежать пересечения загружаемых сегментов, соответствующих разным программам, нужно использовать параметр -Ttext <адрес>, который задает адрес загрузки секции .text. В качестве смещения между адресами загрузки секции .text в разных процессах можно принять значение, превышающее на 1 МБ размер наибольшего исполняемого файла в образе решения. Например, в качестве адреса загрузки секции .text для программы Client в примере ping можно указать значение 0x06000000, а для программы Server в этом примере можно указать значение 0x06200000.

    Смещение между адресами секций .text не может гарантировать отсутствия пересечений загружаемых сегментов, если заранее собранные исполняемые файлы, используемые в решении (например, из состава KasperskyOS Community Edition), скомпонованы для поддержки ASLR. (Отключить ASLR и задать адреса загрузки секций .text можно только для тех исполняемых файлов, сборка которых выполняется при создании решения. Для заранее собранных исполняемых файлов этого сделать нельзя.) Секции .text таких исполняемых файлов загружаются по случайным адресам, что может привести к пересечению с загружаемыми сегментами, соответствующими программе, которую нужно отладить.

  2. [Опционально] Задать параметры запуска QEMU.

    Чтобы изменить начальные и/или добавить новые параметры запуска QEMU, нужно использовать параметр QEMU_FLAGS CMake-команды build_kos_qemu_image().

    Пример:

    set(QEMU_FLAGS "-m 2048 -machine vexpress-a15,secure=on \ -cpu cortex-a72 -nographic -monitor none -smp 4") build_kos_qemu_image(kos-qemu-image ... QEMU_FLAGS "${QEMU_FLAGS}")
В начало
[Topic debugging_qemustub_preparing]

Начальные шаги отладки

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

  1. Собрать отладочные версии исполняемых файлов и библиотек, создать образ решения для QEMU и запустить QEMU в режиме ожидания подключения отладчика GDB.

    Для этого нужно вызвать shell-команды cmake, указав параметры -D CMAKE_BUILD_TYPE:STRING=Debug и --target gdbsim.

    Пример:

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

    Вместо цели gdbsim можно указать цель gdbsim/fast, чтобы не выполнять повторную сборку.

    QEMU запускается, но не исполняет код решения, ожидая вызова GDB-команды continue.

  2. Запустить отладчик GDB и подключиться к GDB-серверу QEMU.

    Для этого нужно вызвать shell-команду make gdb в директории build.

  3. [Опционально] Задать дополнительные пути для поиска динамических библиотек с отладочными символами.

    На шаге 1 в файл build/einit/.gdbinit автоматически добавляется путь, по которому отладчик GDB выполняет поиск динамических библиотек с отладочными символами из состава KasperskyOS Community Edition (в качестве параметра GDB-команды set sysroot). Чтобы задать дополнительные пути для поиска динамических библиотек, нужно вызвать следующую GDB-команду:

    set solib-search-path <путь к директории>...
  4. [Опционально] Загрузить отладочные символы исполняемых файлов.

    Если для исполняемых файлов со статической компоновкой заданы адреса загрузки секции .text (CMake-командой set_target_properties()), то GDB-команда загрузки отладочных символов для каждого из этих файлов автоматически добавляется в файл build/einit/.gdbinit на шаге 1.

    Если для одного исполняемого файла с динамической компоновкой задан адрес загрузки секции .text, то GDB-команда загрузки отладочных символов для этого файла автоматически добавляется в файл build/einit/.gdbinit на шаге 1. Если для нескольких исполняемых файлов с динамической компоновкой заданы адреса загрузки секции .text, то GDB-команда загрузки отладочных символов автоматически не добавляется в файл build/einit/.gdbinit на шаге 1 ни для одного из этих исполняемых файлов.

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

    • add-symbol-file <путь к файлу> – для исполняемых файлов со статической или динамической компоновкой;
    • file <путь к файлу> – для исполняемых файлов с динамической компоновкой.

    GDB-команду file нужно использовать, чтобы отладчик GDB загрузил отладочные символы исполняемого файла и динамических библиотек, от которых зависит этот исполняемый файл. Чтобы отладчик GDB загрузил отладочные символы только исполняемого файла с динамической компоновкой, нужно использовать GDB-команду add-symbol-file.

    GDB-команда file может быть применена только для одного исполняемого файла. То есть отладчик GDB не может загрузить отладочные символы динамических библиотек для нескольких исполняемых файлов одновременно.

    После вызова GDB-команды file может появиться следующее сообщение:

    warning: Unable to find dynamic linker breakpoint function. GDB will be unable to debug shared library initializers and track explicitly loaded dynamic code.

    Это сообщение нужно проигнорировать.

    Если отладочные символы сохраняются не в исполняемых файлах, а в отдельных файлах, то в исполняемые файлы добавляются ссылки на файлы с отладочными символами. При вызове GDB-команды add-symbol-file или file можно указать как исполняемый файл, так и файл с отладочными символами.

При выполнении повторной сборки (шаг 1) необязательно завершать сессию отладки, то есть выходить из отладчика. (Это позволяет избежать повторного выполнения таких действий, как загрузка отладочных символов и задание дополнительных путей для поиска динамических библиотек.) Чтобы не завершать сессию отладки, нужно перед повторной сборкой выполнить GDB-команду detach, а после запуска QEMU выполнить GDB-команду target remote localhost:1234.

GDB-команды (например, для загрузки отладочных символов или задания дополнительных путей для поиска динамических библиотек) можно записать в файл, а затем вызывать их, используя GDB-команду source <путь к файлу>.

В начало
[Topic debugging_qemustub_initsteps]

Особенности отладки при эмуляции аппаратных платформ с SMP

Процессоры (вычислительные ядра) представляются как потоки исполнения.

Чтобы вывести сведения о процессорах, нужно вызвать следующую GDB-команду:

info threads

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

thread <значение Id, полученное вызовом GDB-команды info threads>
В начало
[Topic debugging_qemustub_smp]

Отладка программ с использованием GDB-сервера ядра KasperskyOS

GDB-сервер ядра имеет следующие особенности:

  • Распознает процессы и потоки исполнения, управляемые KasperskyOS.
  • Поддерживает отладку на аппаратных платформах с SMP (в том числе при эмуляции на QEMU).

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

Подготовка к отладке на QEMU

Начальные шаги отладки на QEMU

Подготовка к отладке на аппаратной платформе

Начальные шаги отладки на аппаратной платформе

Работа с процессами и потоками исполнения

В начало
[Topic debugging_kernelstub]

Подготовка к отладке на QEMU

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

  1. Проверить, что в составе KasperskyOS Community Edition есть версия ядра с GDB-сервером.

    Для это нужно убедиться в наличии файла libexec/aarch64-kos/kos-qemu-gdbstub в составе KasperskyOS Community Edition.

  2. Добавить параметр GDBSTUB_KERNEL в список параметров CMake-команды build_kos_qemu_image().

    Этот параметр включает в решение ядро с GDB-сервером.

    Пример:

    build_kos_qemu_image(kos-qemu-image GDBSTUB_KERNEL ... QEMU_FLAGS "${QEMU_FLAGS}")
  3. Создать файл .gdbinit, чтобы не вызывать вручную начальные GDB-команды при каждом запуске отладчика GDB. (Файл .gdbinit нужно сохранить в директории, где будет запускаться отладчик GDB.)

    Пример:

    # Задать путь для поиска динамических библиотек с отладочными символами из # состава KasperskyOS Community Edition set sysroot /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos # Задать дополнительные пути для поиска динамических библиотек с отладочными # символами set solib-search-path /home/user/example/build/lib1:/home/user/example/build/lib2 # Настроить управление потоками исполнения (опционально) # Отладчик GDB может работать в режиме all-stop или non-stop. В первом режиме # отладчик GDB считает, что при остановке потока исполнения, контекст которого # находится в фокусе отладки, останавливаются все остальные потоки исполнения, # о которых известно отладчику GDB. Во втором режиме отладчик GDB считает, что # все потоки, кроме остановленного, продолжают исполняться. Режим all-stop # применяется отладчиком GDB по умолчанию, и GDB-сервер ядра дает возможность # использовать только этот режим. Чтобы изменить заданные по умолчанию параметры # управления потоками исполнения в режиме all-stop, нужно использовать GDB-команды # set scheduler-locking и set schedule-multiple. # В этом примере первая GDB-команда указывает отладчику GDB, что при возобновлении # исполнения потока, контекст которого находится в фокусе отладки (например, # GDB-командами continue, step, next), нужно возобновить исполнение остальных потоков. # Вторая GDB-команда указывает отладчику GDB, что при возобновлении исполнения потока, # контекст которого находится в фокусе отладки, нужно возобновить исполнение всех # потоков, входящих в процессы всех программ, для которых выполняется отладка. set scheduler-locking off set schedule-multiple on # Подключить отладчик GDB к GDB-серверу ядра # GDB-сервер ядра взаимодействует с отладчиком GDB по расширенному протоколу, # и QEMU предоставляет доступ к GDB-серверу ядра через TCP-сокет. target extended-remote localhost:1234
В начало
[Topic debugging_kernelstub_qemu_preparing]

Начальные шаги отладки на QEMU

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

  1. Собрать отладочные версии исполняемых файлов и библиотек, создать образ решения для QEMU и запустить QEMU.

    Для этого нужно вызвать shell-команды cmake, указав параметры -D CMAKE_BUILD_TYPE:STRING=Debug и --target sim.

    Пример:

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

    Вместо цели sim можно указать цель sim/fast, чтобы не выполнять повторную сборку.

    QEMU запускается и исполняет код решения. Исполнение кода решения останавливается при подключении отладчика GDB.

  2. Запустить отладчик GDB и подключиться к GDB-серверу ядра.

    Для этого нужно запустить исполняемый файл toolchain/bin/aarch64-kos-gdb из состава KasperskyOS Community Edition. Запуск нужно выполнить в директории, где сохранен созданный вручную файл .gdbinit.

  3. Создать инфериоры, привязать их к процессам и загрузить отладочные символы.

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

    • add-symbol-file <путь к файлу> – для исполняемых файлов со статической или динамической компоновкой;
    • file <путь к файлу> – для исполняемых файлов с динамической компоновкой.

    GDB-команду file нужно использовать, чтобы отладчик GDB загрузил отладочные символы исполняемого файла и динамических библиотек, от которых зависит этот исполняемый файл. Чтобы отладчик GDB загрузил отладочные символы только исполняемого файла с динамической компоновкой, нужно использовать GDB-команду add-symbol-file.

    Если отладочные символы сохраняются не в исполняемых файлах, а в отдельных файлах, то в исполняемые файлы добавляются ссылки на файлы с отладочными символами. При вызове GDB-команды add-symbol-file или file можно указать как исполняемый файл, так и файл с отладочными символами.

При повторной сборке (шаг 1) нужно завершить сессию отладки (выйти из отладчика GDB) и выполнить повторно шаги 2 и 3.

В начало
[Topic debugging_kernelstub_qemu_initsteps]

Подготовка к отладке на аппаратной платформе

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

  1. Проверить, что в составе KasperskyOS Community Edition есть версии ядра с GDB-сервером.

    Для это нужно убедиться в наличии файла libexec/aarch64-kos/kos-gdbstub в составе KasperskyOS Community Edition.

  2. Скоммутировать компьютер, на котором будет работать отладчик GDB, с аппаратной платформой.

    Для подключения компьютера к аппаратной платформе нужно использовать преобразователи USB-UART. Схемы соединения преобразователей USB-UART и аппаратных платформ см. в "Подготовка Raspberry Pi 4 B к запуску примеров" и "Подготовка Radxa ROCK 3A к запуску примеров".

  3. Добавить параметр GDBSTUB_KERNEL в список параметров CMake-команды build_kos_hw_image().

    Этот параметр включает в решение ядро с GDB-сервером.

    Пример:

    build_kos_hw_image(kos-image GDBSTUB_KERNEL ... IMAGE_FILES ${ENTITIES})
  4. Создать файл .gdbinit, чтобы не вызывать вручную начальные GDB-команды при каждом запуске отладчика GDB. (Файл .gdbinit нужно сохранить в директории, где будет запускаться отладчик GDB.)

    Пример:

    # Задать путь для поиска динамических библиотек с отладочными символами из # состава KasperskyOS Community Edition set sysroot /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos # Задать дополнительные пути для поиска динамических библиотек с отладочными # символами set solib-search-path /home/user/example/build/lib1:/home/user/example/build/lib2 # Настроить управление потоками исполнения (опционально) # Отладчик GDB может работать в режиме all-stop или non-stop. В первом режиме # отладчик GDB считает, что при остановке потока исполнения, контекст которого # находится в фокусе отладки, останавливаются все остальные потоки исполнения, # о которых известно отладчику GDB. Во втором режиме отладчик GDB считает, что # все потоки, кроме остановленного, продолжают исполняться. Режим all-stop # применяется отладчиком GDB по умолчанию, и GDB-сервер ядра дает возможность # использовать только этот режим. Чтобы изменить заданные по умолчанию параметры # управления потоками исполнения в режиме all-stop, нужно использовать GDB-команды # set scheduler-locking и set schedule-multiple. # В этом примере первая GDB-команда указывает отладчику GDB, что при возобновлении # исполнения потока, контекст которого находится в фокусе отладки (например, # GDB-командами continue, step, next), нужно возобновить исполнение остальных потоков. # Вторая GDB-команда указывает отладчику GDB, что при возобновлении исполнения потока, # контекст которого находится в фокусе отладки, нужно возобновить исполнение всех # потоков, входящих в процессы всех программ, для которых выполняется отладка. set scheduler-locking off set schedule-multiple on # Задать символьную скорость для взаимодействия с GDB-сервером ядра # Нужно задать значение 115200 бод. set serial baud 115200 # Подключить отладчик GDB к GDB-серверу ядра # Нужно задать порт USB, через который подключен преобразователь USB-UART, # используемый для отладки. Также необходимо задать, что GDB-сервер ядра # взаимодействует с отладчиком GDB по расширенному протоколу. target extended-remote /dev/ttyUSB1
В начало
[Topic debugging_kernelstub_hw_preparing]

Начальные шаги отладки на аппаратной платформе

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

  1. Собрать отладочные версии исполняемых файлов и библиотек и создать образ решения для аппаратной платформы.

    Для этого нужно вызвать shell-команды cmake, указав параметры -D CMAKE_BUILD_TYPE:STRING=Debug и --target <tgt>, где tgt – значение параметра NAME в CMake-команде build_kos_hw_image().

    Пример:

    #!/bin/bash ... cmake -G "Unix Makefiles" \ -D CMAKE_BUILD_TYPE:STRING=Debug \ -D CMAKE_TOOLCHAIN_FILE=$SDK_PREFIX/toolchain/share/toolchain-$TARGET.cmake \ -B build \ && cmake --build build --target kos-image
  2. Загрузить образ решения на аппаратную платформу и запустить решение.

    Исполнение кода решения останавливается при подключении отладчика GDB.

  3. Запустить отладчик GDB и подключиться к GDB-серверу ядра.

    Для этого нужно запустить исполняемый файл toolchain/bin/aarch64-kos-gdb из состава KasperskyOS Community Edition. Запуск нужно выполнить в директории, где сохранен созданный вручную файл .gdbinit.

  4. Создать инфериоры, привязать их к процессами и загрузить отладочные символы.

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

    • add-symbol-file <путь к файлу> – для исполняемых файлов со статической или динамической компоновкой;
    • file <путь к файлу> – для исполняемых файлов с динамической компоновкой.

    GDB-команду file нужно использовать, чтобы отладчик GDB загрузил отладочные символы исполняемого файла и динамических библиотек, от которых зависит этот исполняемый файл. Чтобы отладчик GDB загрузил отладочные символы только исполняемого файла с динамической компоновкой, нужно использовать GDB-команду add-symbol-file.

    Если отладочные символы сохраняются не в исполняемых файлах, а в отдельных файлах, то в исполняемые файлы добавляются ссылки на файлы с отладочными символами. При вызове GDB-команды add-symbol-file или file можно указывать как исполняемый файл, так и файл с отладочными символами.

При повторной сборке (шаг 1) нужно завершить сессию отладки (выйти из отладчика GDB) и выполнить повторно шаги 2-4.

В начало
[Topic debugging_kernelstub_hw_initsteps]

Работа с процессами и потоками исполнения

В отладчике GDB для работы с процессами используются объекты, которые называются инфериорами (англ. inferiors). Процесс каждой программы, которую нужно отладить, должен быть привязан к отдельному инфериору. Переключение между инфериорами соответствует переключению фокуса отладки в контекст другого процесса. Отладочные символы для программы, которую требуется отладить, нужно загружать после переключения на инфериор, который привязан к процессу этой программы.

При запуске отладчика GDB автоматически создается инфериор. При подключении отладчика GDB к GDB-серверу ядра исполнение кода решения останавливается, и этот инфериор автоматически привязывается к процессу, содержащему поток исполнения, в контексте которого останавливается исполнение кода решения. Остальные требуемые инфериоры могут быть созданы, привязаны к процессам и отвязаны от процессов вручную или автоматически. В первом случае нужно использовать GDB-команды для работы с инфериорами. Во втором случае требуется настроить обработку событий жизненного цикла процессов.

GDB-команды для работы с инфериорами

Для работы с инфериорами нужно использовать следующие GDB-команды:

  • info inferiors

    Выводит список инфериоров. Текущий инфериор отмечается символом *. (Контекст процесса, ассоциированного с текущим инфериором, находится в фокусе отладки.)

  • add-inferior

    Создает инфериор.

  • inferior <номер инфериора>

    Переключает на другой инфериор.

  • attach <PID>

    Привязывает текущий инфериор к процессу с идентификатором PID. Чтобы вывести список процессов, нужно выполнить GDB-команду info os processes.

  • detach

    Отвязывает текущий инфериор от процесса.

Настройка обработки событий жизненного цикла процессов

GDB-сервер ядра сообщает отладчику GDB о наступлении следующих событий:

  1. создание дочернего процесса;
  2. запуск начального потока исполнения дочернего процесса;
  3. завершение процесса.

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

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

  • set follow-fork-mode {child|parent}

    Указывает отладчику GDB перейти в контекст дочернего процесса (child) или остаться в контексте родительского процесса (parent) при наступлении события 1. Второй вариант выполняется по умолчанию.

  • set detach-on-fork {on|off}

    Указывает отладчику GDB оставить родительский или дочерний процесс привязанным к инфериору (on) или оставить родительский процесс привязанным к инфериору, а также создать новый инфериор и привязать его к дочернему процессу (off) при наступлении события 1. Первый вариант выполняется по умолчанию. При этом, какой процесс привязан к инфериору (родительский или дочерний), зависит от параметра GDB-команды set follow-fork-mode.

  • set follow-exec-mode {new|same}

    Указывает отладчику GDB создать новый инфериор и привязать его к дочернему процессу (new) или не делать этого (same) при наступлении события 2. Второй вариант выполняется по умолчанию.

Рекомендуется настроить обработку событий 1 и 2 следующим образом:

set follow-fork-mode parent set follow-exec-mode same set detach-on-fork off

Такая конфигурация обеспечивает обработку событий 1 и 2 отладчиком GDB следующим образом:

  • При наступлении события 1 создается новый инфериор, который привязывается к дочернему процессу.
  • При наступлении события 1 родительский процесс остается привязанным к инфериору.
  • При наступлении события 2 не создается новый инфериор для дочернего процесса.

Обработку отладчиком GDB события 2 можно использовать для остановки исполнения программы до передачи управления функции main(), чтобы не применять бесконечный цикл.

Чтобы исполнение программы остановилось при наступлении события 2, нужно использовать следующую GDB-команду:

catch exec

Эта GDB-команда обеспечивает остановку исполнения каждого дочернего процесса, при создании которого было обработано событие 1. Чтобы выполнить остановку исполнения отдельных процессов, нужно использовать следующую GDB-команду:

monitor exec [<имя процесса 1>][, <имя процесса 2>]...

Последующий вызов этой GDB-команды отменяет предыдущий, а ее вызов без параметров означает остановку каждого дочернего процесса, при создании которого было обработано событие 1.

Событие 3 обрабатывается отладчиком GDB только в том случае, если созданы инфериоры, привязанные к завершившимся процессам. При обработке этого события отладчик GDB отвязывает инфериор от завершившегося процесса. Обработку события 3 не требуется настраивать.

GDB-команды для работы с потоками исполнения

Чтобы вывести список потоков исполнения, которые содержатся в процессах, привязанных к инфериорам, нужно выполнить следующую GDB-команду:

info threads

Текущий поток исполнения отмечается символом *. (Контекст текущего потока исполнения находится в фокусе отладки.)

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

thread <значение Id, полученное вызовом GDB-команды info threads>

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

В начало
[Topic debugging_kernelstub_processes_threads]

Анализ данных обратной трассировки стека при аварийном завершении процесса

При завершении процесса в результате необработанного исключения ядро KasperskyOS выводит данные обратной трассировки стека в следующем виде:

Call Trace (User Mode): [<0000000004068498>] (__assert_func+0x78) from [<000000000405f478>] (_vfs_backend_create+0xe8) [<000000000405f478>] (_vfs_backend_create+0xe8) from [<00000000040bdab0>] (_vfs_init+0xd0) [<00000000040bdab0>] (_vfs_init+0xd0) from [<00000000040111a8>] (init_vfs_helper+0xc8) [<00000000040111a8>] (init_vfs_helper+0xc8) from [<00000000040117e0>] (__eprol+0x88) [<00000000040117e0>] (__eprol+0x88)

Здесь очевидна последовательность вызовов функций, которая привела к аварийному завершению процесса. В конце последовательности код функции _vfs_backend_create(), размещенный в памяти процесса по адресу 0x000000000405f478, вызвал функцию __assert_func(), а код функции __assert_func(), размещенный в памяти процесса по адресу 0x0000000004068498, вызвал исключение. То есть исключение возникло при проверке корректности условий в функции _vfs_backend_create(). (Запись вида <имя функции>+0x<смещение> означает адрес внутри кода функции. Например, запись _vfs_backend_create+0xe8 означает адрес внутри кода функции _vfs_backend_create(), смещенный относительно адреса начала кода этой функции на 0xe8 байт. Запись _vfs_backend_create+0xe8 соответствует адресу 0x000000000405f478.)

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

Пример данных обратной трассировки стека, содержащих только адреса памяти:

Call Trace (User Mode): [<0000000004068498>] from [<000000000405f478>] [<000000000405f478>] from [<00000000040bdab0>] [<00000000040bdab0>] from [<00000000040111a8>] [<00000000040111a8>] from [<00000000040117e0>] [<00000000040117e0>]

Причина отсутствия имен функций в данных обратной трассировки стека заключается в следующем. Для вывода данных обратной трассировки стека ядро получает имена функций из таблицы символов .symtab и таблицы строк .strtab, загруженных в память аварийно завершившегося процесса из отладочной версии исполняемого файла. Эти таблицы могут быть неполными либо вообще отсутствовать в памяти аварийно завершившегося процесса. Также ядро не может вывести имена функций, импортированных из динамических библиотек.

Таблица символов .symtab и таблица строк .strtab отсутствуют в памяти процесса в следующих случаях:

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

    Выпускная версия исполняемого файла, в отличие от отладочной, не содержит таблицу символов .symtab и таблицу строк .strtab.

  • Процесс создан из отладочной версии исполняемого файла, из которого перед добавлением в образ решения были удалены таблица символов .symtab и таблица строк .strtab, например, утилитой strip.
  • Процесс создан из отладочной версии исполняемого файла, из которого перед добавлением в образ решения не были удалены таблица символов .symtab и таблица строк .strtab, но эти таблицы не были загружены в память процесса.

    Создать процесс с опциональной загрузкой таблицы символов .symtab и таблицы строк .strtab позволят высокоуровневый API управления процессами и низкоуровневый API управления процессами.

Таблица символов .symtab и таблица строк .strtab являются неполными в следующих случаях:

  • Если исполняемый файл собран как отладочный, но при сборке в него включены выпускные версии статических библиотек, то в таблице символов .symtab и таблице строк .strtab не будет сведений о функциях, которые реализованы в этих библиотеках.
  • Если исполняемый файл собран как выпускной, но при сборке в него включены отладочные версии статических библиотек, то в таблице символов .symtab и таблице строк .strtab будут сведения только о тех функциях, которые реализованы в этих библиотеках.

Для некоторых адресов памяти, указанных в данных обратной трассировки стека, сведения о функциях можно получить из исполняемого файла. Если адрес памяти присутствует в таблице символов .symtab исполняемого файла, то можно получить номер строки кода и имя функции, соответствующие этому адресу памяти. Если адрес памяти отсутствует в таблице символов .symtab, но присутствует в таблице символов .dynsym исполняемого файла, то можно получить имя функции, соответствующей этому адресу памяти. (При использовании ASLR адреса памяти, указанные в данных обратной трассировки стека, увеличены на значение смещения загрузки ELF-образа Relocation base, которое выводится вместе с данными обратной трассировки стека.) В иных случаях получить из исполняемого файла сведения о функциях нельзя. Например, это относится к функциям, импортируемым из динамических библиотек, а также к функциям, реализованным в выпускных версиях статических библиотек.

Исполняемый файл в KasperskyOS содержит набор экспортируемых функций (хотя не является динамической библиотекой). Например, функции библиотеки libc, которая включается в исполняемые файлы, являются экспортируемыми. Таблица символов .dynsym содержит сведения об экспортируемых функциях. Эта таблица присутствует в выпускной версии исполняемого файла, а также остается в отладочной версии исполняемого файла после удаления из него отладочных символов. (Таблица символов .symtab и таблица строк .strtab являются частью отладочных символов.)

Чтобы получить из исполняемого файла сведения о функциях, соответствующих адресам памяти, указанным в данных обратной трассировки, нужно использовать утилиту addr2line, поставляемую в составе KasperskyOS Community Edition (исполняемый файл toolchain/bin/aarch64-kos-addr2line). Формат команды запуска утилиты addr2line:

aarch64-kos-addr2line [--functions] --exe=<путь к исполняемому файлу> 0x<адрес 1> [0x<адрес 2>]...

В команде нужно указать адреса памяти из данных обратной трассировки стека, уменьшенные на значение Relocation base. Параметр --functions требует вывода имен функций в дополнение к номерам строк исходного кода.

Пример:

$ aarch64-kos-addr2line --functions --exe=./build/hello/Hello 0x30090 main /opt/KasperskyOS-Community-Edition-<version>/examples/hello/hello/src/hello.c:6
В начало
[Topic debugging_backtrace_data_analysis]