Why SBOM is Hard in C++?
One of the biggest misconceptions about SBOMs is assuming that generating an SBOM for C++ should be as straightforward as it is for ecosystems such as JavaScript, Python, or Java. In practice, C++ presents unique challenges that make SBOM generation significantly more difficult.
1. There Is No Single Source of Truth
Modern language ecosystems typically have a centralized dependency management mechanism:
- JavaScript: package.json
- Python: requirements.txt, pyproject.toml
- Java: pom.xml, build.gradle
- Rust: Cargo.toml
In C++, however, there is no universally adopted package management standard. Dependencies may come from multiple sources:
- Package managers such as Conan or vcpkg
- Operating system packages (apt, rpm, brew)
- Git submodules
- Vendored source code copied directly into repositories
- Manually downloaded third-party libraries
- Proprietary internal libraries
As a result, there is often no single manifest file that completely describes all components used by a project.
2. Dependencies Are Determined by the Build System
For many languages, dependencies are explicitly declared in dependency files.
In C++, the actual dependency graph is frequently constructed during the build process.
Build systems commonly used in C++ include:
- CMake
- Meson
- Bazel
- Make
- SCons
- Custom build scripts
Several factors influence which dependencies are ultimately included:
- Conditional compilation
#ifdef ENABLE_SSL
#include <openssl/ssl.h>
#endif
Whether OpenSSL appears in the final build depends entirely on compilation options.
- Platform-specific dependencies
Linux builds may OpenSSL. while Windows builds may rely on Schannel or WinHTTP. Consequently, two builds from the same source tree may produce different SBOMs.
- Build options
-DENABLE_ZLIB=ON
-DENABLE_SQLITE=OFF
These flags directly affect the dependency graph.
3. Static Linking Makes Dependencies Harder to Observe
Static linking is very common in C++ projects. The final executable may contain code originating from multiple libraries, but those libraries no longer exist as separate runtime artifacts.
Challenges include:
- Runtime scanners may not detect statically linked components.
- Determining whether a vulnerable library version is embedded inside a binary can be difficult.
- Unused portions of a static library may be removed by the linker.
4. Vendored Dependencies Are Difficult to Identify
Many C++ projects include third-party source code directly in their repositories. Problems introduced by vendoring include:
- Missing package metadata
- Missing version information
- Local patches applied by developers
- Unclear provenance
Questions become difficult to answer:
- Which version of zlib is this?
- Was it modified?
- Is it affected by known vulnerabilities?
SBOM generation tools may need to infer component identity from:
- File signatures
- Source code fingerprints
- Heuristic matching
These approaches are inherently less reliable.
5. Toolchain Dependencies Are Often Overlooked
C++ software depends not only on libraries but also on the toolchain used to build it.
Examples include:
- Compiler versions
- Linkers
- Standard libraries
- SDKs
Examples:
- GCC 13
- Clang 18
- MSVC 2022
- glibc
- musl
- Windows SDK
A vulnerability in the compiler or standard library may affect the generated binaries, yet many SBOM generation approaches omit these components.
Final Thought
The fundamental challenge of SBOM generation in C++ is that there is no single, complete representation of what a C++ application is made of. Dependencies are distributed across package managers, build systems, source repositories, toolchains, and compiled artifacts.
The most accurate SBOM strategy for C++ is therefore not to rely on a single data source, but to combine build-time metadata, package manager information, and artifact analysis to produce the most comprehensive view possible.
You can also check my course about SBOM for C++ to get more informations regarding theory, SBOM generation using Conan, and binary analysis techniques for C++.