Contents
Debugging programs using the GDB server of the KasperskyOS kernel
The GDB server of the kernel has the following distinguishing characteristics:
- Recognizes processes and threads managed by KasperskyOS.
- Supports debugging on hardware platforms with SMP (including for emulation in QEMU).
Preparations for debugging in QEMU
To prepare the conditions for debugging with the GDB server of the KasperskyOS kernel in QEMU, you must complete the following steps:
- Verify that KasperskyOS Community Edition contains a kernel version with the GDB server.
To do so, make sure that the
libexec/aarch64-kos/kos-qemu-gdbstub
file is included in KasperskyOS Community Edition. - Add the
GDBSTUB_KERNEL
parameter to the list of parameters of theCMake
commandbuild_kos_qemu_image()
.This parameter includes a kernel with the GDB server in the solution.
Example:
build_kos_qemu_image(kos-qemu-image GDBSTUB_KERNEL ... QEMU_FLAGS "${QEMU_FLAGS}") - Create a
.gdbinit
file so that you do not need to manually call the initial GDB commands every time the GDB debugger is started. (The.gdbinit
file must be saved in the directory where the GDB debugger will be run.)Example:
# Define a path to search for dynamic libraries containing debug symbols from # KasperskyOS Community Edition set sysroot /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos # Define additional paths to search for dynamic libraries containing debug # symbols set solib-search-path /home/user/example/build/lib1:/home/user/example/build/lib2 # Configure thread management (optional) # The GDB debugger can operate in all-stop or non-stop mode.In the first mode, # when the GDB debugger sees the stoppage of the thread whose context # is in the debug focus, it assumes that all other threads # known to the GDB debugger also stop. In the second mode, the GDB debugger assumes that # all threads except the stopped thread continue to run. All-stop mode # applied by the GDB debugger by default, and the GDB server of the kernel provides the capability to use # only this mode. To edit the default settings # for managing threads in all-stop mode, use GDB commands # set scheduler-locking and set schedule-multiple. # In this example, the first GDB command indicates that, when resuming # execution of a thread whose context is in the debug focus (for example, # the following GDB commands: continue, step, next), the GDB debugger must resume execution of all other threads. # The second GDB command indicates that the GDB debugger, when resuming execution of a thread # whose context is in the debugging focus, must resume execution of all # threads included in the processes of all programs that are being debugged. set scheduler-locking off set schedule-multiple on # Connect the GDB debugger to the GDB server of the kernel # The GDB server of the kernel interacts with the GDB debugger via an extended protocol, # and QEMU provides access to the kernel GDB server via the TCP socket. target extended-remote localhost:1234
Initial steps of debugging in QEMU
To begin debugging using the GDB server of the KasperskyOS kernel in QEMU, you must complete the following steps:
- Build debug versions of executable files and libraries, create a solution image for QEMU, and run QEMU.
To do so, call the
cmake
shell commands, and specify the-D CMAKE_BUILD_TYPE:STRING=Debug
and--target sim
parameters.Example:
"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... cmake -GInstead of the
sim
target, you can specify thesim/fast
target to avoid rebuilding.QEMU starts and executes the solution code. Execution of the solution code stops when the GDB debugger connects.
- Run the GDB debugger and connect to the GDB server of the kernel.
To do so, run the executable file
toolchain/bin/aarch64-kos-gdb
from KasperskyOS Community Edition. It must be run in the directory where the manually created.gdbinit
file was saved. - Create inferiors, attach them to processes, and load debug symbols.
To load debug symbols, you need to switch to the inferior associated with the process and use the following GDB commands:
add-symbol-file
<path to file
> — for executable files with static or dynamic linking.file
<path to file
> – for executable files with dynamic linking.
The GDB command
file
must be used so that the GDB debugger can load debug symbols of an executable file and the dynamic libraries required by this executable file. Use the GDB commandadd-symbol-file
for the GDB debugger to load the debug symbols only for an executable file with dynamic linking.If debug symbols are saved in separate files instead of in the executable files, links to the files containing the debug symbols are added to the executable files. When the GDB command
add-symbol-file
orfile
is called, you can specify the executable file or the file containing debug symbols.
When performing a repeat build (step 1), you need to terminate the debug session (exit the GDB debugger) and repeat steps 2 and 3.
Preparations for debugging on the hardware platform
To prepare the conditions for debugging with the GDB server of the KasperskyOS kernel on the hardware platform, you must complete the following steps:
- Verify that KasperskyOS Community Edition contains kernel versions with the GDB server.
To do so, make sure that the
libexec/aarch64-kos/kos-gdbstub
file is included in KasperskyOS Community Edition. - Establish a switched connection between the hardware platform and the computer where the GDB debugger will operate.
You must use USB-UART converters to connect the computer to the hardware platform. The diagrams for connecting USB-UART converters and hardware platforms are provided in the Preparing Raspberry Pi 4 B to run examples and Preparing Radxa ROCK 3A to run examples sections.
- Add the
GDBSTUB_KERNEL
parameter to the list of parameters of theCMake
commandbuild_kos_hw_image()
.This parameter includes a kernel with the GDB server in the solution.
Example:
build_kos_hw_image(kos-image GDBSTUB_KERNEL ... IMAGE_FILES ${ENTITIES}) - Create a
.gdbinit
file so that you do not need to manually call the initial GDB commands every time the GDB debugger is started. (The.gdbinit
file must be saved in the directory where the GDB debugger will be run.)Example:
# Define a path to search for dynamic libraries containing debug symbols from # KasperskyOS Community Edition set sysroot /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos # Define additional paths to search for dynamic libraries containing debug # symbols set solib-search-path /home/user/example/build/lib1:/home/user/example/build/lib2 # Configure thread management (optional) # The GDB debugger can operate in all-stop or non-stop mode.In the first mode, # when the GDB debugger sees the stoppage of the thread whose context # is in the debug focus, it assumes that all other threads # known to the GDB debugger also stop. In the second mode, the GDB debugger assumes that # all threads except the stopped thread continue to run. All-stop mode # applied by the GDB debugger by default, and the GDB server of the kernel provides the capability to use # only this mode. To edit the default settings # for managing threads in all-stop mode, use GDB commands # set scheduler-locking and set schedule-multiple. # In this example, the first GDB command indicates that, when resuming # execution of a thread whose context is in the debug focus (for example, # the following GDB commands: continue, step, next), the GDB debugger must resume execution of all other threads. # The second GDB command indicates that the GDB debugger, when resuming execution of a thread # whose context is in the debugging focus, must resume execution of all # threads included in the processes of all programs that are being debugged. set scheduler-locking off set schedule-multiple on # Define the symbol rate for interaction with the GDB server of the kernel # You must set the value to 115200 baud. set serial baud 115200 # Connect the GDB debugger to the GDB server of the kernel # You must define the USB port that is used to connect the USB-UART converter # used for debugging. You must also specify that the GDB server of the kernel # interacts with the GDB debugger via an extended protocol. target extended-remote /dev/ttyUSB1
Initial steps of debugging on the hardware platform
To begin debugging using the GDB server of the KasperskyOS kernel on the hardware platform, you must complete the following steps:
- Build debug versions of executable files and libraries and create a solution image for the hardware platform.
To do so, call the
cmake
shell commands and specify the-D CMAKE_BUILD_TYPE:STRING=Debug
and--target
<tgt
> parameters, wheretgt
is the value of theNAME
parameter in theCMake
commandbuild_kos_hw_image()
.Example:
"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... cmake -G - Load the solution image to the hardware platform and start the solution.
Execution of the solution code stops when the GDB debugger connects.
- Run the GDB debugger and connect to the GDB server of the kernel.
To do so, run the executable file
toolchain/bin/aarch64-kos-gdb
from KasperskyOS Community Edition. It must be run in the directory where the manually created.gdbinit
file was saved. - Create inferiors, attach them to processes, and load debug symbols.
To load debug symbols, you need to switch to the inferior associated with the process and use the following GDB commands:
add-symbol-file
<path to file
> — for executable files with static or dynamic linking.file
<path to file
> – for executable files with dynamic linking.
The GDB command
file
must be used so that the GDB debugger can load debug symbols of an executable file and the dynamic libraries required by this executable file. Use the GDB commandadd-symbol-file
for the GDB debugger to load the debug symbols only for an executable file with dynamic linking.If debug symbols are saved in separate files instead of in the executable files, links to the files containing the debug symbols are added to the executable files. When the GDB command
add-symbol-file
orfile
is called, you can specify the executable file or the file containing debug symbols.
When performing a repeat build (step 1), you need to terminate the debug session (exit the GDB debugger) and repeat steps 2–4.
Working with processes and threads
In the GDB debugger, objects called inferiors are used to work with processes. The process of each program to be debugged must be attached to a separate inferior. Switching between inferiors is equivalent to switching the debug focus to the context of another process. The debug symbols for the program to be debugged must be loaded after switching to the inferior that is attached to the process of this program.
An inferior is automatically created when the GDB debugger is started. When the GDB debugger connects to the GDB server of the kernel, execution of the solution code stops and this inferior is automatically attached to the process containing the thread in whose context the solution code execution is stopped. All other required inferiors can be created, attached to processes, and detached from processes manually or automatically. In the first case, you need to use the GDB commands for working with inferiors. In the second case, you must configure handling of events in the process life cycle.
GDB commands for working with inferiors
To work with inferiors, you need to use the following GDB commands:
info inferiors
Displays the list of inferiors. The current inferior is marked by the
*
character. (The context of the process associated with the current inferior is in the debug focus.)add-inferior
Creates an inferior.
inferior
<inferior number
>Switches to another inferior.
attach
<PID
>Attaches the current inferior to the process with the specified PID. To print a list of processes, run the GDB command
info os processes
.detach
Detaches the current inferior from the process.
Configuring handling of events in the process life cycle
The GDB server of the kernel notifies the GDB debugger about the occurrence of the following events:
- Creation of a child process
- Start of the initial thread of a child process
- Termination of a process
The GDB debugger handles event 1 only if an inferior is attached to the parent process. To handle event 2, the GDB debugger must have previously handled event 1 when the child process was created. In this case, event 2 is handled even if the parent process was already detached from the inferior, for example, due to its termination.
To configure handling of events 1 and 2, use the following GDB commands:
set follow-fork-mode
{child
|parent
}Instructs the GDB debugger to switch to the context of the child process (
child
) or to remain in the context of the parent process (parent
) when event 1 occurs. The second option is carried out by default.set detach-on-fork
{on
|off
}Instructs the GDB debugger to leave the parent or child process attached to the inferior (
on
) or to leave the parent process attached to the inferior and create a new inferior and attach it to the child process (off
) when event 1 occurs. The first option is carried out by default. In this case, the parameter of the GDB commandset follow-fork-mode
determines which process is attached to the inferior (parent or child).set follow-exec-mode
{new
|same
}Instructs the GDB debugger to create a new inferior and attach it to the child process (
new
) or refrain from doing so (same
) when event 2 occurs. The second option is carried out by default.
It is recommended to configure handling of events 1 and 2 as follows:
This configuration ensures that events 1 and 2 are handled by the GDB debugger as follows:
- When event 1 occurs, a new inferior is created and attached to the child process.
- When event 1 occurs, the parent process remains attached to the inferior.
- When event 2 occurs, a new inferior is not created for the child process.
To avoid using an infinite loop, handling of event 2 by the GDB debugger can be used to stop execution of the program before management is transferred to the main()
function.
To stop execution of the program when event 2 occurs, use the following GDB command:
This GDB command stops the execution of each child process that was created while event 1 was handled. To stop the execution of individual processes, use the following GDB command:
A subsequent call of this GDB command cancels the previous call, and calling this command without parameters will stop each child process that was created while event 1 was handled.
Event 3 is handled by the GDB debugger only if inferiors attached to terminated processes are created. When this event is handled, the GDB debugger detaches the inferior from the terminated process. Handling of event 3 does not need to be configured.
GDB commands for working with threads
To print a list of the threads contained in processes attached to inferiors, run the following GDB command:
The current thread is marked by the *
character. (The context of the current thread is in the debug focus.)
To switch to the context of another thread, use the following GDB command:
Switching to the context of another thread will result in switching to another inferior if this thread is in a process attached to an inferior different from the current one.
Page top