
Generating an SBOM for a web application is often straightforward. A package manager has already resolved the dependency graph, assigned names and versions, and recorded it in a lockfile. An SBOM generator mainly has to translate that structured data into CycloneDX or SPDX.
Embedded firmware is different.
A typical firmware image may combine generated code, a vendor SDK, an RTOS, middleware, copied source trees, static archives, compiler runtime libraries, proprietary binary blobs, and application code. Some components are selected in an IDE. Others appear only in compiler commands, linker scripts, or the final linker map. There may be no universal package manifest—and no single file that tells the whole truth.
The recurring questions in the STMicroelectronics, NXP, and Nordic communities all point to the same problem:
How do I create an SBOM for the firmware I actually ship—not merely for the SDK directory from which I started?
This Q&A answers that question and the difficult ones behind it.
FAQ
What is an embedded or IoT SBOM?
An embedded SBOM is a machine-readable inventory of the software components associated with a specific device firmware release.
At minimum, it should identify the product and release, the included third-party and open-source components, their versions and suppliers, and the relationships between them. Where possible, it should also include licenses, hashes, external identifiers such as Package URLs, and evidence explaining how each component was identified.
The important words are specific firmware release. An inventory of everything available in a vendor SDK is useful supplier information, but it is not automatically the SBOM for your product.
Why are SBOMs easier for web, mobile, and desktop applications?
Modern application ecosystems usually provide three things:
Standard manifests and lockfiles such as
package-lock.json,pom.xml,packages.lock.json, orpoetry.lockCentral registries that provide normalized package names, versions, licenses, and identifiers
Package managers that resolve direct and transitive dependencies before the build
Embedded C and C++ have no universal equivalent. Dependencies may be copied into Drivers/, added as Git submodules, delivered as ZIP archives, selected through a graphical configurator, or linked from a proprietary toolchain.
The dependency graph exists, but it is scattered across project files, build commands, include paths, generated configuration, archives, and linker output.
Does a vendor-provided SDK SBOM solve the problem?
It solves part of the problem.
ST now publishes CycloneDX SBOMs with a growing number of STM32Cube packages; for example, the STM32CubeF1 repository contains an sbom_cdx.json. This is valuable because the supplier is best placed to identify the components, versions, origins, and licenses in its own distribution.
But an SDK SBOM answers:
What components did the vendor deliver in this SDK release?
Your product SBOM must answer:
Which components, versions, patches, configurations, and binary objects were used in this firmware build?
Those are not the same question. A full STM32Cube package may contain drivers for many boards, examples you never compile, several middleware options, and evaluation components that never enter your product. Your team may also patch a HAL driver, replace a middleware version, or add an RTOS and proprietary library.
Treat the supplier SBOM as authoritative upstream metadata. Reconcile it with your build and link evidence to produce the product SBOM.
Can STM32CubeMX generate the final SBOM for my firmware?
STM32CubeMX metadata is useful, but configuration intent alone is not enough.
The .ioc file can identify information such as:
MCU family and part
STM32Cube firmware package and version
CubeMX version
Configured middleware
Project and toolchain selections
This is strong context. It can tell an SBOM generator that a project targets STM32F4 and declares a particular STM32Cube firmware package. It should not, by itself, prove that every configured middleware component reached the final binary.
Projects drift. Developers regenerate only part of a project, modify generated files, upgrade middleware manually, exclude files in the IDE, or retain an old .ioc file after changing the build.
The reliable order of evidence is:
Supplier metadata tells you what the SDK contains.
Project and configurator metadata tells you what the build intends to use.
Compiler and build-system metadata tells you what was compiled.
Linker evidence tells you what reached the firmware artifact.
Manual, reviewed input closes the remaining gaps.
Why is scanning the source directory not enough?
A directory scan sees availability, not usage.
If a repository contains FreeRTOS, lwIP, Mbed TLS, FatFs, USB host, USB device, and dozens of board examples, a whole-tree scanner may report all of them. That creates false positives: components that exist in the source tree but are not part of the shipped firmware.
The opposite problem also occurs. A source scan may miss:
Precompiled
.aarchivesProprietary binary libraries
Compiler runtime libraries
Code generated outside the repository
Dependencies referenced through an external SDK path
Objects introduced only during the link
Source scanning remains useful for identifying vendored code and extracting license evidence. It becomes much more reliable when constrained by the build graph and reconciled with the final link.
Should unused or linker-discarded components appear in the SBOM?
There are two defensible approaches.
For a customer-facing product SBOM, teams often omit components proven not to be present in the shipped artifact. This keeps vulnerability results focused on the product.
For engineering and audit workflows, retaining those components with an explicit excluded scope can be valuable. It preserves the evidence trail and explains why a component visible in the project does not appear as shipped.
The important rule is not to silently mix “present,” “available,” “optional,” and “excluded.” Scope should be explicit and supported by evidence.
How should statically linked libraries be represented?
A static library should be represented as a component when it has a meaningful identity: name, version, supplier or origin, and ideally a hash or external identifier.
The hard part is proving whether code from the archive was selected. Merely seeing libfoo.a on the linker command line does not prove that the linker extracted a member from it. A linker map can often show which archive members contributed to the final image.
When a static archive is proprietary or lacks version metadata, record at least:
Supplier and library name
Release or delivery identifier
SHA-256 hash
Source of supply
License classification
Support or end-of-life information, when available
If the archive changes while the filename remains the same, the hash becomes especially important.
What about proprietary binary blobs, SoftDevices, radio stacks, and bootloaders?
They belong in the SBOM when they are part of the delivered product, even if their internals are opaque.
The Nordic community discussion around the older nRF5 SDK captures the practical reality: third-party scanning can help, but precompiled libraries and details not discovered automatically may require manual entry. A binary component is not exempt merely because source code is unavailable.
For an opaque component, capture the best available identity:
Field | Why it matters |
|---|---|
Name | Stable human-readable identity |
Version or release ID | Update and vulnerability correlation |
Supplier | Ownership and escalation path |
SHA-256 | Exact identity of the delivered bytes |
Delivery source | Procurement and audit traceability |
License | Usage and redistribution obligations |
Product or part number | Correlation with supplier documentation |
Support end date | Long-term product risk |
Supplier SBOM or VEX reference | Downstream vulnerability context |
A component with limited metadata is still better than an invisible component.
Do compilers, IDEs, debuggers, and flashing tools belong in the SBOM?
Separate software in the product from software used to build the product.
The compiler, IDE, code generator, debugger, and flashing utility usually do not ship inside the device firmware. They should not be represented as ordinary runtime components of the product. They are still important build dependencies and should be recorded in build provenance or as build-tool relationships.
Compiler runtime code is different. Libraries such as libgcc, C runtime libraries, IAR DLIB variants, or other runtime support may be statically linked into the firmware. Those bytes are part of the artifact and should be considered for the product SBOM.
This distinction prevents two common mistakes: omitting linked runtime code and falsely claiming that an IDE or debugger is installed in the device.
What about generated code?
Generated code should be traced to both the delivered source files and the generator context.
For example, CubeMX-generated initialization code may be customized after generation. The product SBOM should identify the relevant STM32Cube package and linked HAL or middleware components, while build provenance records the CubeMX version and configuration used.
Do not treat “generated by tool X” as a substitute for component identity. The generator, template or SDK package, selected modules, local modifications, and final linked result are separate facts.
How do I handle patches to vendor or open-source code?
Keep the upstream identity, then record that the component is modified.
A practical record includes:
Upstream component name and version
Upstream repository or supplier
Exact local commit or source hash
Patch set or internal change reference
License
Relationship to the product release
Do not invent a new version that looks like an upstream release. Use a local qualifier or property that clearly identifies the modified state. This preserves vulnerability matching while making it clear that the bytes are not identical to the supplier release.
What should I do when a component has no version?
Do not guess.
Try, in order:
Supplier release notes or package metadata
Git tag and commit
Source headers or version macros
Archive path and toolchain metadata
Cryptographic hash plus delivery identifier
A reviewed manual override
If a version remains unknown, say so and emit a diagnostic. A transparent unknown is more defensible than a confident but incorrect version.
Are headers dependencies, components, or just files?
It depends on their role.
Headers may be:
Part of a larger component already represented in the SBOM
Header-only libraries that are compiled into the application
Generated configuration inputs
Public interfaces for a binary library
Unused files merely present in an SDK
Listing every header as a top-level software component usually produces noise. File-level SBOM detail can be useful for evidence or audits, but component identity should generally remain at the library, SDK module, RTOS, middleware, or application-module level.
For header-only code, compile dependency data and include paths can provide stronger evidence than a directory scan alone.
How should I generate SBOMs for different firmware variants?
Generate an SBOM for each shippable configuration.
A Debug build, Release build, bootloader, application image, board revision, region-specific radio configuration, and feature-flag combination may contain different components. One “master SBOM” for the repository cannot reliably represent all of them.
Each SBOM should be tied to:
Product and firmware version
Build ID or artifact hash
Target MCU or hardware revision
Build configuration
Feature or product variant
Source revision
Toolchain version
Generation timestamp
For fleets, preserve the mapping from device or device cohort to the firmware artifact and SBOM. This is what lets a security team answer, “Which deployed devices contain the affected component?”
What about devices that remain in the field for 10 or 20 years?
Long product life makes SBOM management a release-engineering discipline, not a one-time compliance task.
Archive the SBOM with every released artifact, including hotfixes and backports. Keep the source revision, build configuration, compiler and linker versions, supplier SBOMs, manual additions, and hashes required to reproduce or explain the inventory.
The SBOM should remain available even after the original SDK download, CI image, developer workstation, or supplier portal has disappeared.
Does the EU Cyber Resilience Act require an SBOM?
The EU Cyber Resilience Act requires manufacturers to draw up an SBOM in a commonly used, machine-readable format covering at least the product’s top-level dependencies as part of vulnerability-handling obligations. The regulation applies generally from 11 December 2027, while manufacturer reporting obligations under Article 14 apply from 11 September 2026. The authoritative text is Regulation (EU) 2024/2847.
Two nuances matter.
First, the CRA does not say that every SBOM must automatically be published to every user. User instructions must say where the SBOM can be accessed if the manufacturer chooses to make it available to users, while authorities can request relevant technical documentation and SBOM information under the regulation.
Second, “top-level dependencies” is a legal minimum, not necessarily an operationally sufficient inventory. A manufacturer responding to a critical vulnerability will often need deeper component and transitive-dependency visibility than the minimum alone provides.
This article is engineering guidance, not legal advice. Product scope, conformity assessment, sector-specific rules, and contractual delivery obligations should be reviewed with qualified counsel.
Who is responsible when suppliers do not provide an SBOM?
The manufacturer of the final product still needs a defensible inventory and vulnerability-management process for what it integrates.
Supplier SBOMs are the preferred starting point because suppliers know their own components best. Procurement and engineering contracts should request:
CycloneDX or SPDX in a machine-readable format
One SBOM per delivered artifact or release
Stable version and artifact hashes
Supplier and source information
License data
Security support period
Vulnerability disclosure and update commitments
VEX for known high-impact vulnerabilities, where appropriate
If a supplier provides only a PDF or spreadsheet, preserve it as source evidence but normalize the component data into the release SBOM process. Make machine-readable delivery a contractual target rather than leaving the format ambiguous.
Is manual SBOM data acceptable?
Yes—when it is structured, version-controlled, reviewed, and repeatable.
Manual work is not the enemy. Untracked manual work is.
Maintain manual additions as a separate YAML, JSON, or other reviewable input in version control. Give each component a minimum identity, record why it was added, and merge it with automatically discovered components during the release pipeline.
This is appropriate for:
Proprietary archives and blobs
Purchased protocol stacks
SDKs with missing metadata
Patched components
Closed PLC or OT libraries
Components delivered outside the normal source repository
The preferred output is one unified SBOM per release. If organizational constraints require separate generated and manually curated SBOMs, version them together and assemble or reference them as one release set.
Are Conan, vcpkg, PlatformIO, Zephyr, and Yocto easier?
They provide more structure, but the final artifact still matters.
Conan and vcpkg improve component naming and version resolution for dependencies they manage. They do not automatically describe manually copied source, proprietary archives, or every toolchain runtime.
PlatformIO centralizes framework and library metadata, but teams should still verify that declared libraries match the selected environment and final firmware.
Zephyr and west provide manifest-based source revision control across modules. This is a strong foundation for an SBOM, especially when combined with the configured build and link output.
Yocto is one of the most mature embedded Linux ecosystems for SBOM generation. Its
create-spdxclass can generate SPDX information for images and SDKs from the build system.
The general rule remains: use package or manifest metadata for identity, build metadata for selection, and artifact evidence for shipped scope.
What about PLC and closed OT engineering environments?
Closed OT environments often expose less build detail than MCU or embedded Linux workflows. Dependencies may be hidden inside proprietary project containers, vendor libraries, function blocks, or engineering-tool exports.
Use the strongest evidence the platform exposes:
Exported library inventories
Project or automation APIs
Vendor-supplied SBOMs
Signed library packages and hashes
Release manifests
A version-controlled manual component list
Do not claim artifact-level precision when the engineering environment cannot provide it. State the evidence source and confidence level, and improve the process through supplier requirements and supported export interfaces.
What is a practical SBOM workflow for embedded firmware?
Use a layered workflow:
Build the exact release configuration. Do not generate the final SBOM from an arbitrary developer checkout.
Collect supplier SBOMs and release metadata. Preserve their original files.
Read project and build-system metadata. Include selected targets, configurations, source files, include paths, archives, and toolchain settings.
Generate a linker map. Reconcile declared or compiled inputs with what reached the artifact.
Identify vendored source. Map copied source trees and submodules to upstream components and versions.
Add opaque components manually. Record hashes, suppliers, versions, licenses, and delivery references.
Generate CycloneDX or SPDX. Validate the document against the relevant schema.
Review diagnostics and unknowns. Do not treat file creation as proof of SBOM quality.
Tie the SBOM to the artifact. Store the firmware hash, build ID, source revision, and variant.
Archive, monitor, and update. Use the SBOM for vulnerability analysis and produce VEX when a vulnerability is not exploitable.
How does lynkctl generate an embedded C/C++ SBOM?
lynkctl reads the build evidence that embedded projects already produce. It supports GNU Make, CMake, and IAR Embedded Workbench projects, including .ewp and .eww files. For STM32CubeMX projects, .ioc metadata adds package and target context, while build and link evidence remain authoritative for component scope.
It does not rebuild the project. Run it after the normal release build so the configured build tree, artifacts, and linker map are available.
For a GNU Make firmware project:
For CMake:
For IAR Embedded Workbench:
When a third-party source tree needs explicit component boundaries:
When authoritative metadata is missing or needs correction, use a version-controlled override:
The result is not merely a list of files. It is a component inventory derived from the selected build, enriched with component identity, and backed by evidence that can be reviewed when a customer, auditor, or security engineer asks, “Why is this component in the SBOM?”
What does a high-quality embedded SBOM look like?
A high-quality embedded SBOM is:
Artifact-specific: tied to the firmware that was released
Configuration-specific: tied to the board, target, and feature set
Build-aware: derived from actual project and compiler inputs
Link-aware: distinguishes shipped code from dead or unused code
Supplier-informed: reuses authoritative upstream metadata
Honest about uncertainty: records evidence, confidence, and unknowns
Complete across opaque inputs: includes proprietary and precompiled components
Reproducible: can be regenerated and meaningfully diffed
Operational: feeds vulnerability monitoring, VEX, customer delivery, and incident response
The hardest part of embedded SBOM generation is not producing valid JSON. It is making a defensible claim about what is in a particular firmware image.
That is why the right embedded SBOM process combines automation with engineering evidence. Automate what the toolchain can prove. Preserve what suppliers know. Add opaque components through controlled manual inputs. Then repeat the process for every release and every shippable variant.
Final question: where should a team start?
Start with one production firmware target and one release configuration.
Enable a linker map. Gather the relevant SDK and supplier metadata. Generate the first SBOM from the completed build. Review every unknown component and false positive with the firmware team. Put the corrections under version control, and make the same workflow part of the release pipeline.
You do not need perfect automation on day one. You need a process that is accurate, reviewable, and better on every release.
If you are trying to generate an SBOM from STM32CubeMX, MCUXpresso, an nRF firmware project, GNU Make, CMake, or IAR Embedded Workbench, Interlynk’s embedded C/C++ SBOM tooling is built for that gap between the SDK you received and the firmware you actually ship.