Der Stand der SBOM-Erstellung für C/C++: Ausgabe 2026
Ritesh Noronha

Wenn du denkst, dass die SBOM-Erstellung ein gelöstes Problem ist, arbeitest du wahrscheinlich nicht in C/C++.
Ein guter SBOM-Generator sollte drei Dinge gut beherrschen: genau sein, reproduzierbar sein und das darstellen, was tatsächlich in der Binärdatei oder Firmware steckt, die du auslieferst. Für Ökosysteme mit ausgereiften Paketmanagern und maschinenlesbaren Metadaten zu Abhängigkeiten ist das deutlich einfacher. JavaScript hat package-lock.json, Python hat oft poetry.lock, Rust hat Cargo.lock und Go hat go.sum. Diese Dateien machen die SBOM-Erstellung nicht trivial, aber sie bieten in der Regel einen wesentlich klareren Ausgangspunkt als C/C++.
Für C/C++ haben wir keinen solchen Luxus. Und in der Embedded-Welt, in der C/C++ dominiert, ist die Lücke zwischen dem, was SBOM-Tools erzeugen, und dem, was tatsächlich in deiner Firmware steckt, groß genug, um mit einem Lastwagen hindurchzufahren.
Ich habe viel Zeit damit verbracht, SBOM-Tools für eingebettete C/C++-Projekte zu entwickeln, und ich möchte eine ehrliche Einschätzung darüber teilen, wo die Lage heute steht — was funktioniert, was nicht, und warum ein kombinierter Ansatz der einzige realistische Weg zu einer einigermaßen genauen SBOM ist.
Die Geschichte der verwalteten Abhängigkeiten: Besser, aber nicht das ganze Bild
Tools wie CMake, Conan, vcpkg und Meson haben der C/C++-Abhängigkeitsverwaltung etwas Struktur verliehen. Wenn Ihr Projekt Conan verwendet, können Sie eine conan.lock-Datei erhalten. Wenn Sie vcpkg im Manifest-Modus verwenden, haben Sie zumindest ein vcpkg.json-Manifest und Versionsbeschränkungen. CMake-FetchContent-Deklarationen dokumentieren ebenfalls, was zur Konfigurationszeit eingebunden wird.
Einige SBOM-Tools können diese Metadaten nutzen und einen brauchbaren Ausgangspunkt für zumindest einen Teil des Abhängigkeitsgraphen erzeugen. Für den Teil der C/C++-Welt, der moderne Abhängigkeitsverwaltung konsequent übernommen hat, ist die SBOM-Situation besser als früher, auch wenn sie immer noch unvollständig ist.
Dieser Teil ist klein, besonders im Embedded-Bereich. Die meisten eingebetteten C/C++-Projekte, denen ich begegne, werden nicht mit Conan oder vcpkg erstellt. Sie werden mit einfachen Makefiles erstellt — manchmal von Hand geschrieben, manchmal von einer IDE wie IAR Embedded Workbench oder STM32CubeIDE erzeugt. Die Verbreitung von CMake wächst im Embedded-Bereich, ist aber noch längst nicht universell. Und selbst Projekte, die CMake verwenden, haben oft eine Mischung aus FetchContent, Systembibliotheken, Git-Submodulen und mitgeliefertem Quellcode, die kein einzelnes Tool vollständig erfasst.
Der Ansatz mit dem `-l`-Flag: Nützlich, aber unzureichend
Eine gängige Technik zum Identifizieren von C/C++-Abhängigkeiten besteht darin, die Linker-Eingaben zu prüfen — insbesondere -l-Flags und Linker-Suchpfade. Wenn Ihr Build gegen -lssl -lcrypto -lz linkt, kann ein Tool möglicherweise OpenSSL und zlib ableiten und dies mit Metadaten des Paketmanagers anreichern, wenn diese Bibliotheken aus dem Betriebssystem oder einer bekannten Paketquelle stammen. Dies kann sowohl dynamische Bibliotheken (.so / .dylib) als auch statische Archive (.a) abdecken, wenn der Build sie ausdrücklich benennt.
Für Desktop- und Server-Builds kann man mit diesem Ansatz recht weit kommen. Die -l-Flags in Kombination mit Linker-Suchpfaden (-L) verraten oft, welche Bibliotheken eingebunden werden. Auf diese Weise verknüpfte statische .a-Archive sind ebenfalls sichtbarer als mitgelieferter Quellcode, der direkt ins Ziel kompiliert wird.
Aber selbst wenn dieser Ansatz eine Komponente erfolgreich identifiziert, gibt es ein tieferliegendes Problem: Er liefert oft einen Bibliotheksnamen und manchmal eine Version, aber nicht viel mehr. Zu den Mindestbestandteilen der NTIA gehören Lieferantenname, Komponentenname, Version, eindeutige Kennungen, Abhängigkeitsbeziehungen, Autor der SBOM-Daten und Zeitstempel. Ein -l-Flag kann Ihnen einen Bibliotheksnamen nennen. Eine .a-Datei kann Ihnen vielleicht nur einen Dateinamen wie libfoo.a liefern, mit wenig oder gar keiner maschinenlesbaren Herkunft darüber, wer sie gebaut hat, woher sie stammt oder welchen Upstream-Release sie repräsentiert. Wenn die Bibliothek nicht aus einer Paketquelle mit Metadaten stammt, müssen die übrigen NTIA-Felder in der Regel auf andere Weise rekonstruiert werden — und bei den meisten C/C++-Projekten gibt es keinen automatisierten „anderen Weg“.
Im Embedded-Bereich ist die Lage deutlich schlimmer. Embedded-Firmware-Builds verwenden oft überhaupt keine -l-Flags, die auf systeminstallierte Pakete verweisen. Stattdessen kompilieren sie das RTOS, das HAL, den Netzwerk-Stack, die Kryptobibliothek und Middleware aus dem Quellcode — oder sie linken gegen projektlokale .a-Dateien, die zuvor aus mitgeliefertem Code mit nur wenig angehängten Metadaten gebaut wurden. Das resultierende Binary ist häufig monolithisch. Es kann keine Laufzeitabhängigkeitsinformationen im ldd-Stil geben, die man prüfen könnte, keine DT_NEEDED-Einträge in ELF-Headern, und gestrippte Symbole verringern, was eine Binäranalyse wiederherstellen kann. Eine statische .a-Datei im lib/-Verzeichnis eines Projekts, die Monate zuvor aus einer unbekannten Upstream-Version gebaut wurde, ist für SBOM-Zwecke nahezu eine Blackbox — und eine Compliance-Lücke für jede Regulierung, die mehr als nur einen Komponentennamen verlangt.
Vendored Code: Die eigentliche Herausforderung
Hier wird es wirklich schwierig, und hier denke ich, unterscheidet sich das C/C++-SBOM-Problem am stärksten von Ökosystemen mit starken Paketmanager-Konventionen.
Vendor-Code ist in C/C++ überall. Entwickler kopieren Quelldateien — manchmal nur ein einzelnes .c- und .h-Paar, manchmal ganze Bibliotheksbäume — direkt in ihr Projekt. Bibliotheken wie nlohmann/json, mongoose, stb_image, miniz und lwIP werden routinemäßig auf diese Weise integriert. Ihre eigene Dokumentation sagt Ihnen oft genau das: „Kopieren Sie diese beiden Dateien in Ihr Projekt.“ Einmal kopiert, ist der Code kaum von eigenem Quellcode zu unterscheiden.
Fingerprinting ist eine der wichtigsten Techniken zur Erkennung von vendored Code, und für die einfachen Fälle funktioniert es gut. Wenn ein Entwickler eine unveränderte Kopie einer eigenständigen Bibliothek vendored hat, können Sie die Dateien hashen, mit einem Korpus bekannter Releases vergleichen und oft eine sichere Übereinstimmung erhalten. Für kleine, in sich geschlossene Bibliotheken kann das ziemlich zuverlässig sein.
Aber das Vendoring bringt Komplikationen mit sich, die Fingerprinting zu einem heuristischen statt zu einem deterministischen Verfahren machen.
Modifikationen sind häufig. Entwickler vendoren Code und ändern ihn dann — beheben Fehler, passen APIs an, entfernen ungenutzte Funktionalität. Sobald eine Datei geändert wurde, schlägt exaktes Hash-Matching fehl. Dann bewegen Sie sich im Bereich des unscharfen Matchings: Vergleich mit bekannten Versionen, Techniken wie MinHash oder Winnowing zur Code-Ähnlichkeit oder struktureller Vergleich auf AST-Ebene. Diese Ansätze können funktionieren, bringen aber Unsicherheit mit sich. Handelt es sich um eine modifizierte Kopie von FreeRTOS 10.4.3 oder um FreeRTOS 10.5.1 mit anderen Änderungen? Die Antwort ist für die CVE-Korrelation wichtig, und die Tools können es Ihnen oft nicht mit Sicherheit sagen.
Vendor-SDKs bringen zusätzliches Rauschen mit sich. Halbleiterhersteller wie ST, NXP, TI und Renesas liefern SDKs aus, die Open-Source-Komponenten — FreeRTOS, lwIP, mbedTLS, FatFS — zusammen mit proprietärem HAL-Code bündeln. Diese Pakete fügen oft herstellerspezifische Copyright-Header hinzu, ändern Verzeichnisstrukturen, benennen Dateien um und forken den Upstream-Code manchmal erheblich. STM32CubeF4 enthält eine Version von FreeRTOS, die den Anpassungsprozess von ST durchlaufen hat. Renesas FSP bündelt ein ThreadX-Derivat. Diese gegen Upstream-Releases zu fingerprinten ist ohne herstellerspezifische Erkennungslogik unzuverlässig.
Einbettungsmodelle und ML-basierte Code-Ähnlichkeit können helfen, und es gibt interessante akademische Arbeiten zur Verwendung gelernter Code-Repräsentationen für die Identifikation von Softwarekomponenten. Aber heute sind diese Ansätze für die routinemäßige SBOM-Erstellung in CI in großem Maßstab meist zu aufwendig im Betrieb. Sie können bei einmaligen Audits oder hochsicheren Untersuchungen eine Rolle spielen, sind aber noch kein einfacher Ersatz für einfachere Techniken.
Das Problem des geteilten Lieferantenordners
Es gibt eine praktische Schwierigkeit, die in SBOM-Gesprächen meiner Meinung nach nicht oft genug diskutiert wird. Die meisten C/C++-Entwickler halten ihren zugekauften Code in separaten Ordnern — vendor/, 3rdparty/, external/, lib/ — was die Verwaltung erleichtert. Das ist gute Hygiene und hilft SBOM-Tools dabei, die Grenze zwischen Eigen- und Fremdcode zu erkennen.
In der Praxis jedoch, besonders in Embedded-Teams, wird ein Vendor-Ordner oft von mehreren Projekten oder Build-Zielen gemeinsam genutzt. Ein vendor/-Verzeichnis könnte FreeRTOS, lwIP, mbedTLS, FatFS und ein Dutzend weiterer Bibliotheken enthalten. Projekt A verwendet FreeRTOS und lwIP. Projekt B verwendet FreeRTOS und mbedTLS. Projekt C verwendet sie alle.
Ein SBOM-Generator, der einfach den gesamten Vendor-Ordner inventarisiert, erzeugt eine SBOM, die alle zugekauften Komponenten auflistet — aber diese SBOM ist für jedes einzelne Build-Artefakt falsch. Sie stellt dar, dass mehr enthalten ist, als tatsächlich in der Binärdatei steckt. Wenn Projekt A mbedTLS nicht verwendet, erzeugt dessen Auflistung in der SBOM von Projekt A Fehlalarme bei Vulnerability-Scans und stellt die tatsächliche Angriffsfläche falsch dar.
In vielen Embedded-Projekten ist der zuverlässigste Weg, dies richtig zu machen, zu verstehen, was das Build-System für ein bestimmtes Ziel tatsächlich kompiliert und verlinkt. Damit kommen wir wieder zum Build-System als einer zentralen Quelle der Wahrheit zurück, und warum Build-Zeit-Analysen für Embedded C/C++ in der Regel unerlässlich sind.
Die reale Welt ist chaotisch: Ein kombinierter Ansatz
Keine einzelne Technik löst das C/C++-SBOM-Problem. Nachdem ich die verschiedenen Ansätze durchgearbeitet habe — Manifest-Parsing, Inspektion von Linker-Flags, Datei-Fingerprinting, Binäranalyse, Instrumentierung des Build-Systems — bin ich zu der Überzeugung gelangt, dass ein kombinierter Ansatz der einzige Weg zu einer hinreichend genauen SBOM ist. Konkret müssen vier Signale zusammengeführt werden:
Was gebaut wird. Das Build-System — ob Make, CMake, Meson, IAR oder etwas anderes — weiß, welche Quelldateien kompiliert und welche Bibliotheken für ein gegebenes Target gelinkt werden. Wenn man den Build-Prozess instrumentiert, erhält man belastbare Informationen darüber, was in das Binärfile eingeht. Das ist die Grundlage.
Was eingebunden wurde. Verzeichnis-Fingerprinting, Datei-Hashing und das Extrahieren von Versionszeichenfolgen können bekannte Open-Source-Komponenten in Vendor-Verzeichnissen identifizieren. Der Schlüssel ist, dies mit dem abzugleichen, was das Build-System tatsächlich verwendet — nicht nur mit dem, was auf der Festplatte vorhanden ist. Eine Komponente in vendor/, die nie in Ihr Target kompiliert wird, sollte nicht in Ihrer SBOM auftauchen.
Für welche Plattform oder welchen Mikrocontroller es gebaut wird. Die Zielplattform schränkt ein, welche Vendor-SDKs und HAL-Bibliotheken im Spiel sind. Wenn Sie für einen STM32F4 bauen, wissen Sie, dass STM32CubeF4-HAL-Komponenten wahrscheinlich vorhanden sind. Wenn Sie auf einen Renesas RA6M4 abzielen, schauen Sie sich Renesas FSP an. Plattformkenntnis hilft dabei, den Suchraum einzugrenzen und herstellerspezifische Erkennungsheuristiken anzuwenden. Sie hilft auch bei der CPE/PURL-Zuordnung, da die Benennung eingebetteter Komponenten stark herstellerspezifisch ist.
Verwaltung statischer Abhängigkeiten. Das Verständnis dafür, wie statische Bibliotheken (.a, .lib) gelinkt werden, welche Objektdateien sie enthalten und woher sie stammen, vervollständigt das Bild. Map-Dateien (.map), Linker-Skripte und Archivinhalte liefern alle Hinweise. Bei IAR-Projekten listen .map-Dateien jede Objektdatei auf, die in das finale Image gelinkt wurde. Bei GCC-basierten Builds zeigt ar -t auf statischen Archiven deren Inhalte an.
Jedes dieser Signale allein ist unvollständig. Zusammen können sie Sie zu einer genaueren SBOM führen, und das ist in der Regel das, was für das Schwachstellenmanagement, die Beschaffung und die regulatorische Berichterstattung zählt.
Entwicklerkontext ist immer noch wichtig
Die automatisierte Erkennung ist wichtig, aber bei C/C++-Projekten, besonders eingebetteten, ist der vom Entwickler bereitgestellte Kontext weiterhin sehr wichtig. Das Entwicklungsteam weiß oft, welche eingebundene Bibliothek übernommen wurde, woher sie stammt, welche Patches angewendet wurden und welche Komponenten zu welcher Build-Variante gehören. Dieser Kontext ist genau die Art von Information, die automatisierte Scanner nur schwer zuverlässig aus Quellbäumen, Archiven und entschlackten Binärdateien rekonstruieren können.
Das bedeutet nicht, dass manuelle oder vom Entwickler gepflegte Manifeste eine Wunderlösung sind. Sie können veralten, Abhängigkeiten übersehen oder von der Realität abweichen, wenn sie nicht eng mit dem Build-Prozess verknüpft sind. In der Praxis können sie jedoch eine nützliche Ergänzung zur automatisierten Analyse sein, insbesondere für Provenienz-, Lieferanten-, Lizenz- und Build-Varianten-Metadaten, die im Nachhinein nur schwer abzuleiten sind.
Strukturierte Komponenten-Metadaten sind die fehlende Ebene in SBOMs — und Interlynk hilft dabei, sie zu definieren. Mit der neuen SBOM-Generierungsfunktion von sbomasm ermöglichen wir Teams, Entwicklerkontext dort zu erfassen, wo er entsteht, und machen automatisierte Scans und Buildzeit-Analysen deutlich zuverlässiger.
Wie geht es von hier aus weiter?
Das C/C++-SBOM-Problem wird nicht durch ein einzelnes Werkzeug oder eine einzelne Technik gelöst werden. Es erfordert einen mehrschichtigen Ansatz, der Buildsystem-Intelligenz, Erkennung eingebundener Drittcodes, plattformbewusste Heuristiken und von der Community gepflegte Korpora bekannter eingebetteter Bibliotheken und ihrer Fingerabdrücke kombiniert.
Auch die eingebettete C/C++-Community muss in eine bessere Abhängigkeitsdisziplin investieren — Paketmanager dort einzuführen, wo es praktikabel ist, explizite Manifeste für eingebundenen Drittcode zu pflegen und die SBOM-Erzeugung als erstklassiges Build-Artefakt statt als nachträglichen Gedanken zu behandeln.
Für diejenigen von uns, die in diesem Bereich Werkzeuge entwickeln, ist die Chance klar: Wer die Lücke zwischen der Unordnung realer C/C++-Projekte und den strukturierten, präzisen SBOMs, die Regulierungen verlangen, zuverlässig schließen kann, wird eines der schwierigsten verbleibenden Probleme der Software-Lieferkettensicherheit lösen.
Und das wird eine SBOM sein, der Sie vertrauen und die Sie verifizieren können.
Zitationen
CMake, „FetchContent“, offizielle Dokumentation: https://cmake.org/cmake/help/latest/module/FetchContent.html
NTIA, „Die Mindestanforderungen für eine Software Bill of Materials (SBOM)“, Juli 2021: https://www.ntia.gov/sites/default/files/publications/sbom_minimum_elements_report_0.pdf
Europäische Kommission, „Cyber Resilience Act - Umsetzung“, abgerufen am 9. April 2026: https://digital-strategy.ec.europa.eu/en/factpages/cyber-resilience-act-implementation
FDA, „Medical Device Software Guidance Navigator“, einschließlich „Cybersicherheit in Medizinprodukten: Überlegungen zum Qualitätsmanagementsystem und Inhalt von Vorab-Einreichungen“: https://www.fda.gov/medical-devices/regulatory-accelerator/medical-device-software-guidance-navigator
Das Weiße Haus, „Executive Order on Improving the Nation's Cybersecurity“ (Executive Order 14028), 12. Mai 2021: https://www.whitehouse.gov/briefing-room/presidential-actions/2021/05/12/executive-order-on-improving-the-nations-cybersecurity/
sbomasm, „Ihr SBOM-Assembler“ : https://github.com/interlynk-io/sbomasm