Basics and targets

Dependencies

Sentry build system is based on Meson, but also needs some external python tooling. The external dependencies are checked by meson in order to detect if they are properly installed. If not, the build system will inform the user that there is a missing dep in the build environment.

The Sentry dependencies are the following:

  • meson: the effective Sentry build system. This is a python module. that can be installed with pip.

  • ninja: build system used as backend by meson. Can be replaced with another if needed. This is a python module.

  • kconfiglib: This is the Kconfig configuration parser and manager that respect the kconfig syntax that has been intially defined by the Linux kernel and which is documented here.

  • device-tree-compiler: This is the device tree compiler tool, that compile device tree sources into device tree blobs. This tool is a part of the kernel.org projects and is packaged in all GNU/Linux distros.

  • dtschema: This is python toolset to manipulate and validate device trees. This is a python module that can be installed with pip.

  • sphinx: Used in order to generate this very documentation. This is a python module.

  • sphinx_rtd_theme: Theme used for Sentry documentation. This is a python module.

  • exhale: Python tool that convert Doxygen XML output into sphinx ReST content automatically. It is used to generate the complete source documentations with sphinx output.

  • sphinx-simplepdf: sphinx extention to generate the documentation in its PDF format. This is a python module.

  • weasyprint: widechar compatible python module that help in generating pdf files. Dependency of the sphinx-simplepdf module. This is a python module.

  • svd2json: Tool that convert SVD files into json dictionary in order to allow header generation in meson build system directly from SVD files. This is a python module.

  • dts-utils: Tool that convert device tree file into a dictionary with some helpers that can be used to generate header files. This is used by jinja to forge autogenerated headers from the project device tree. This is a python module.

  • jinja-cli: This is the jinja implementation that allows to generate template in any language using an efficient templating language with various builtins, intially designed for web services and now used widespread. This is a python module.

  • rustup: This is the Rust ecosystem bootsraper that allows toolchain deployment. When installed, the Sentry build system automatically fetch the correct toolchain version for the configured target if needed. Rustup can be found on Rustup website.

  • C cross-toolchain: This is the C toolchain to be used for the cross-compilation. Like the Rust toolchain downloaded, it will be used at compile time, and it is checked by the build system to be sure that it is able to cross-compile correctly the Sentry C standard required (C11). For example, ARM toolchains can be found on ARM website.

  • native C/C++ toolchain: This is used for unit-testing, in order to compile and execute unit tests. Unit tests are using the gtest/gmock framework. The build system checks if the gtest framework is installed and fallback to a local download if needed. This toolchain can be found as a part of all GNU/Linux distributions.

The meson build system automatically check that all python modules are installed on the build system, but in order to simplify the setup phase, a requirements.txt file has been added to the kernel root source path.

As a consequence, before building the project, one needs to install meson, and then all python dependencies are described in requirements.txt.

pip3 install -r requirements.txt

Warning

For non-python dependencies, the user needs to check and install himself the build dependencies in the way he wants.

About Sentry sources hierarchy

The Sentry kernel sources is divided in three main blocks:

  • The Sentry kernel itself, hosted in the kernel/ subdir

  • The Sentry UAPI, hosted in the uapi/ subdir

  • The Sentry idle task, hosted in the idle/ subdir

Sentry kernel core

Built by default: true

Output: kernel/sentry-kernel.elf

The Sentry kernel core is written in both C and Rust and is built by default. It is possible to deactivate the kernel build with the following option:

-Dwith_kernel=false

The sentry kernel is composed of separated elements that are built as libraries, alowing easy testing, correctness checking and portability.

To achieve that, the Sentry kernel is decomposed of the following libraries:

  • libsentry_arch: This library hold all the architecture relative implementation. This library API is defined in the <sentry/arch> subdirectory of the kernel include path.

  • libsentry_bsp: This library hold all the device drivers implementation. This library API is defined in the <bsp> subdirectory of the kernel include path.

  • libsentry_sched: This library hold schedulers implementation. Its API is hold in the <sentry/sched.h> header file.

  • libsentry_managers: This library hold the portable, high level implementation of The kernel features, denoted managers. This library API is defined in the <sentry/managers> kernel include path.

  • libsentry_zlib: This library hold portable-C, generic base tooling to be used kernel-wide. This library API is defined in the <sentry/zlib> subdirectory of the kernel include path.

  • libsentry_syscalls: This library hold the Sentry, portable, kernel syscall gate (not including supervisor call handler, that is a part of libsentry_arch). This library API is defined in the single header <sentry/syscalls.h> of the kernel include path.

Sentry UAPI

Built by default: true

Output: uapi/libuapi.a

This is the Sentry UAPI userspace library. This library is fully written in Rust, but export its headers in C to allow both C and Rust implementation.

It is possible to deactivate the libuapi build with the following option:

-Dwith_uapi=false

Sentry Idle task

Built by default: true

Output: idle/idle.elf

This is the Sentry Idle task, used as fallback when no user task is eligible. This task request a low power enter mode, while not forbidden by another task.

It is possible to deactivate the libuapi build with the following option:

-Dwith_idle=false

Sentry Autotest task

Built by default: N/A

Output: autotest/autotest.elf

This is the Sentry utotest task, used in order to execute runtime autotest suite on the target device. This task is build only when the Sentry kernel configuration is in autotest mode (i.e. using the CONFIG_BUILD_TARGET_AUTOTEST build target in the config file or through the menuconfig interface). This task is not build in any of the other build targets (debug or release).

Cross-compilation concept

As the cross-toolchain installation and configuration is not a build-system related content but a build-host related information, the meson build systems is using cross-files to define the current build host toolchain configuration that need to be used for the project.

A typical cross-file defines all the toolchain binaries to use and would look like the following:

 1[constants]
 2cross_triple = 'arm-none-eabi'
 3cross_toolchain = '/opt/arm-none-eabi/'
 4cross_compile = cross_toolchain + 'bin/' + cross_triple + '-'
 5
 6[host_machine]
 7system = 'baremetal'
 8cpu_family = 'arm'
 9cpu = 'cortex-m4'
10endian = 'little'
11exe_wrapper = 'qemu-arm-static'
12
13[binaries]
14c = cross_compile + 'gcc'
15cpp = cross_compile + 'g++'
16ar = cross_compile + 'gcc-ar'
17ranlib = cross_compile + 'gcc-ranlib'
18strip = cross_compile + 'strip'
19objcopy = cross_compile + 'objcopy'
20clang = 'clang'
21rust_ld = cross_compile + 'gcc'
22rust = ['rustc', '--target', 'thumbv7m-none-eabi']

Note

A repository hosting various cross-files, denoted meson-cross-files, exists in the Camelot organisation. Although, anyone can write its own toolchain for its own host, like, for e.g. on Windows build environment:

 1[constants]
 2cross_triple = 'arm-none-eabi'
 3sysroot = 'c:/program files (x86)/arm gnu toolchain arm-none-eabi/12.2 rel1/arm-none-eabi'
 4
 5[host_machine]
 6system = 'baremetal'
 7cpu_family = 'arm'
 8endian = 'little'
 9cpu = 'cortex-m4'
10
11[binaries]
12c = cross_triple + '-gcc'
13cpp = cross_triple + '-g++'
14ar = cross_triple + '-gcc-ar'
15ranlib = cross_triple + '-gcc-ranlib'
16strip = cross_triple + '-strip'
17objcopy = cross_triple + '-objcopy'
18clang = 'clang'
19rust_ld = cross_triple + '-gcc'
20rust = ['rustc', '--target', 'thumbv7m-none-eabi']
21
22[properties]
23bindgen_clang_arguments = [ '--sysroot=' + sysroot, '--target=' + cross_triple ]

Bootstraping Sentry build

A common good practice is do not inject environment variable for build configuration. For this purpose, meson does not allow using relative path in toolchain definition. Toolchain path _must_ be absolute.

One needs to deliver to the meson build system the kernel configuration based on Kconfig. The configuration is forged at project level, using, among others, the Sentry kernel Kconfig entry.

Although, the global project config file generation is under the project responsability, and the Sentry kernel build system consider that this file is built when starting. This is a requirement in order to keep the configuration phase, under Kconfig responsablity, separated from the build phase of each project component, including the Sentry kernel itself. As the configuration phase is handled at project level, the project configuration(s) must be kept somewhere and passed to the kernel build system at setup time.

Modifying the configuration can be done at project level, upgrading or creating new defconfig files, so that the Sentry kernel setup phase can get back the newly created configuration. This part is out of this documentation though and is explained in the project generator documentation.

Here are all the Sentry kernel custom command line options:

  • config: string: declare a project defconfig file that can be used by the Kernel as input

  • with_docs: boolean: activate doc build targets

  • with_proof: boolean: activate formal proof build and exec targets

  • with_tests: boolean: activate gtest unit test framework build and exec

All options can be passed using the widely used -Doption=value argument passing. See meson build system manual to see all possible options that can be transmitted.

Building Sentry

Sentry build is decomposed into two main components:

  • libsentry.a, a static containing all the Sentry components but the entrypoint and the ldscript. This lib is composed of:

    • libsysgate, a static library of the Rust implementation of the syscalls

    • Sentry static C sources

    • Sentry generated sources (from SVD and DTS files)

    • Sentry generated headers (from SVD and DTS files)

    libsentry sources list varies depending on the passed configuration, as all arch-dependant and SoC-dependant sources (such as drivers) are dynamically selected by the build system based on the current project configuration, namely the current SoC name, familly, subfamilly, and the current selected features-set (e.g. debug or release).

  • sentry-kernel.elf, kernel executable, including libsentry, the entrypoint, linked using the Sentry ldscript

When setuping the project, the build system shows the current Sentry project configuration state:

$ meson setup -Dkconfig:config=configs/stm32f429i_disc1_defconfig -Dwith_doc=true --cross-file /workspace/arm-none-eabi-gcc.ini builddir
The Meson build system
Version: 1.2.2
Source dir: /workspace/sentry-kernel/sentry-kernel
Build dir: /workspace/sentry-kernel/builddir
Build type: cross build
Project name: sentry-kernel
Project version: undefined
C compiler for the host machine: /opt/arm-none-eabi/bin/arm-none-eabi-gcc (gcc 12.2.1 "arm-none-eabi-gcc (Arm GNU Toolchain 12.2.Rel1 (Build arm-12.24)) 12.2.1 20221205")
C linker for the host machine: /opt/arm-none-eabi/bin/arm-none-eabi-gcc ld.bfd 12.2
C++ compiler for the host machine: /opt/arm-none-eabi/bin/arm-none-eabi-g++ (gcc 12.2.1 "arm-none-eabi-g++ (Arm GNU Toolchain 12.2.Rel1 (Build arm-12.24)) 12.2.1 20221205")
C++ linker for the host machine: /opt/arm-none-eabi/bin/arm-none-eabi-g++ ld.bfd 12.2
C compiler for the build machine: cc (gcc 11.4.0 "cc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0")
C linker for the build machine: cc ld.bfd 2.38
C++ compiler for the build machine: c++ (gcc 11.4.0 "c++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0")
C++ linker for the build machine: c++ ld.bfd 2.38
Build machine cpu family: x86_64
Build machine cpu: x86_64
Host machine cpu family: arm
Host machine cpu: cortex-m4
Target machine cpu family: arm
Target machine cpu: cortex-m4
Program objcopy found: YES
Program python3 (dunamai) found: YES (/bin/python3) modules: dunamai
[...]
Message: build targetting SoC STM32F429
../meson.build:200: WARNING: !!! This is NOT a release build ! DO NOT USE IT IN PRODUCTION !!!
Build targets in project: 33

sentry-kernel undefined

Configuration
    soc           : stm32f429
    dts           : dts/sentry_stm32f429i_disc1.dts

Subprojects
    cmsis         : YES
    devicetree    : YES
    kconfig       : YES
    meson-svd     : YES

User defined options
    Cross files   : /workspace/arm-none-eabi-gcc.ini
    with_doc      : true
    kconfig:config: configs/stm32f429i_disc1_defconfig

Building the Sentry kernel is as easy as calling Ninja:

ninja -C builddir all

Testing Sentry

Sentry kernel unit testing is using the gtest framework. All unit tests are executed as x86_64 userspace code, meaning that all Sentry code blocks that are executed under test are compiled and executed as x86_64 code.

Calling only a given est suite is then supported through:

meson test -C builddir --suite <suite>

Executing the test suite generates test report. SonarQube XML test report can be generated for SonarQube input.

A typical test execution is the following:

meson test -C builddir
[...]
[61/62] Running all tests.
1/5 sentry-kernel:ut-utils / io               OK              0.03s
2/5 sentry-kernel:ut-utils / bits             OK              0.02s
3/5 sentry-kernel:ut-bsp / exti               OK              0.01s
4/5 sentry-kernel:ut-managers / printk        OK              0.01s
5/5 sentry-kernel:ut-managers / task          OK              0.01s

Ok:                 5
Expected Fail:      0
Fail:               0
Unexpected Pass:    0
Skipped:            0
Timeout:            0

Note

More about the way unit testing Sentry is designed in described in a dedicated chapter.

Prooving Sentry

Sentry kernel is using Frama-C framework in order to include noRTE and functional correctness into the Kernel. For correctness analysis, Sentry is using:

  • EVA (Evaluated Value Analysis) and RTE (Run Time Error) plugins to determine any potential RTE for all possible inputs

  • WP (Weakest Precondition) plugin to validate subprogram contracts based on ACSL specifications of their behavior

Calling Frama-C analysus is done using proof dedicated est suite denoted proof, while the with_proof option is set to true.

meson test -C builddir --suite proof

Executing the test frama-C analysisgenerates a lot of reports, measurement, analysis in the kernel/proof build subdirectory.

A typical test execution is the following:

meson test -C builddir --suite proof
[1/1] Generating kernel/proof/framac.dep with a custom command
1/10 frama-C-parsing                              OK               20.04s
2/10 frama-c-eva-entrypoint                       OK              170.30s
3/10 frama-C-eva-handler-systick                  OK               66.74s
4/10 frama-c-eva-handler-svc                      OK              212.47s
5/10 frama-c-eva-zlib                             OK                1.25s
6/10 frama-c-eva-entrypoint-redalarm              OK                0.03s
7/10 frama-C-eva-handler-systick-redalarm         OK                0.04s
8/10 frama-c-eva-handler-svc-redalarm             EXPECTEDFAIL      0.03s   exit status 10
9/10 frama-c-eva-zlib-redalarm                    OK                0.03s
10/10 frama-c-wp-bsp-rcc                           OK               57.52s

Ok:                 9
Expected Fail:      1
Fail:               0
Unexpected Pass:    0
Skipped:            0
Timeout:            0

Note

More about the way unit testing Sentry is designed in described in a dedicated chapter.