De stand van zaken van SBOM-generatie voor C/C++: editie 2026
Ritesh Noronha

Als je denkt dat het genereren van een SBOM een opgelost probleem is, werk je waarschijnlijk niet in C/C++.
Een goede SBOM-generator zou drie dingen goed moeten doen: nauwkeurig zijn, reproduceerbaar zijn, en weergeven wat er daadwerkelijk in de binaire file of firmware zit die je uitbrengt. Voor ecosystemen met volwassen package managers en machineleesbare afhankelijkheidsmetadata is dit aanzienlijk eenvoudiger. JavaScript heeft package-lock.json, Python heeft vaak poetry.lock, Rust heeft Cargo.lock en Go heeft go.sum. Die bestanden maken het genereren van een SBOM niet triviaal, maar ze bieden meestal een veel duidelijker startpunt dan C/C++.
Voor C/C++ hebben we dat voorrecht niet. En in de embedded wereld, waar C/C++ domineert, is de kloof tussen wat SBOM-tools produceren en wat er daadwerkelijk in je firmware zit groot genoeg om er met een vrachtwagen doorheen te rijden.
Ik heb veel tijd besteed aan het bouwen van SBOM-tooling voor embedded C/C++-projecten, en ik wil een eerlijke beoordeling delen van waar het landschap vandaag de dag staat — wat werkt, wat niet werkt, en waarom een gecombineerde aanpak de enige realistische weg is naar een redelijk nauwkeurige SBOM.
Het verhaal van beheerde afhankelijkheden: beter, maar niet het hele plaatje
Tools zoals CMake, Conan, vcpkg en Meson hebben enige structuur gebracht in afhankelijkheidsbeheer voor C/C++. Als je project Conan gebruikt, kun je een conan.lock-bestand krijgen. Als je vcpkg in manifestmodus gebruikt, heb je ten minste een vcpkg.json-manifest en versiebeperkingen. CMake's FetchContent-declaraties documenteren ook wat er tijdens de configuratiefase wordt binnengehaald.
Sommige SBOM-tools kunnen deze metadata gebruiken en een redelijk startpunt opleveren voor ten minste een deel van de afhankelijkheidsgrafiek. Voor het deel van de C/C++-wereld dat moderne afhankelijkheidsbeheer consequent heeft omarmd, is het SBOM-verhaal beter dan vroeger, ook al is het nog steeds onvolledig.
Die groep is klein, vooral in embedded systemen. De meeste embedded C/C++-projecten die ik tegenkom, worden niet met Conan of vcpkg gebouwd. Ze worden gebouwd met eenvoudige Makefiles — soms handgeschreven, soms gegenereerd door een IDE zoals IAR Embedded Workbench of STM32CubeIDE. De adoptie van CMake groeit in de embedded wereld, maar is nog lang niet universeel. En zelfs projecten die CMake gebruiken, hebben vaak een mix van FetchContent, systeembibliotheken, Git-submodules en meegeleverde broncode die geen enkele tool volledig in kaart brengt.
De benadering met de `-l`-vlag: nuttig, maar onvoldoende
Een veelgebruikte techniek om C/C++-afhankelijkheden te identificeren is het inspecteren van de invoer voor de linker — vooral -l-vlaggen en zoekpaden van de linker. Als uw build koppelt tegen -lssl -lcrypto -lz, kan een tool OpenSSL en zlib afleiden en dat verrijken met metadata van de pakketbeheerder wanneer die bibliotheken afkomstig waren van het besturingssysteem of een bekende pakketbron. Dit kan zowel dynamische bibliotheken (.so / .dylib) als statische archieven (.a) omvatten wanneer de build ze expliciet benoemt.
Voor desktop- en server-builds kan deze aanpak u redelijk ver brengen. De -l-vlaggen, gecombineerd met zoekpaden van de linker (-L), vertellen vaak welke bibliotheken worden meegetrokken. Statische .a-archieven die op deze manier worden gelinkt, zijn ook zichtbaarder dan meegeleverde broncode die rechtstreeks in het doel wordt gecompileerd.
Maar zelfs wanneer deze aanpak erin slaagt een component te identificeren, is er een dieper probleem: vaak krijg je alleen een bibliotheeknaam, en soms een versie, maar niet veel meer. De minimumelementen van NTIA omvatten de naam van de leverancier, de componentnaam, de versie, unieke identificaties, afhankelijkheidsrelaties, de auteur van de SBOM-gegevens en een tijdstempel. Een -l-vlag kan u een bibliotheeknaam geven. Een .a-bestand kan u misschien alleen een bestandsnaam geven zoals libfoo.a, met weinig of geen machineleesbare herkomstinformatie over wie het heeft gebouwd, waar het vandaan kwam of welke upstream-release het vertegenwoordigt. Als de bibliotheek niet afkomstig was van een pakketbron met metadata, moeten de resterende NTIA-velden meestal op een andere manier worden achterhaald — en voor de meeste C/C++-projecten bestaat er geen geautomatiseerde "andere manier."
In de embedded wereld is de situatie aanzienlijk erger. Embedded firmware-builds gebruiken vaak helemaal geen -l-vlaggen die naar systeembreed geïnstalleerde pakketten verwijzen. In plaats daarvan compileren ze de RTOS, HAL, netwerkstack, cryptobibliotheek en middleware vanuit broncode — of ze linken tegen project-lokale .a-bestanden die eerder zijn gebouwd uit meegeleverde code met weinig aangehechte metadata. De resulterende binary is vaak monolithisch. Er is mogelijk geen runtime-afhankelijkheidsinformatie in ldd-stijl om te inspecteren, geen DT_NEEDED-vermeldingen in ELF-headers, en gestript symbols verminderen wat binaire inspectie kan achterhalen. Een statisch .a-bestand dat in de lib/-directory van een project staat en maanden eerder is gebouwd uit een onbekende upstream-versie, is voor SBOM-doeleinden bijna een black box — en een nalevingskloof voor elke regelgeving die meer eist dan een componentnaam.
Ingekochte code: De echte uitdaging
Dit is waar het echt moeilijk wordt, en waar ik denk dat het C/C++ SBOM-probleem het scherpst verschilt van ecosystemen met sterke conventies voor package managers.
Code van derden zit overal in C/C++. Ontwikkelaars kopiëren bronbestanden — soms een enkel .c- en .h-paar, soms complete bibliotheekbomen — rechtstreeks in hun project. Bibliotheken zoals nlohmann/json, mongoose, stb_image, miniz en lwIP worden op deze manier regelmatig geïntegreerd. Hun eigen documentatie zegt vaak letterlijk dat je precies dit moet doen: "kopieer deze twee bestanden naar je project." Zodra ze zijn gekopieerd, is de code niet meer te onderscheiden van broncode van de eerste partij.
Fingerprinting is een van de belangrijkste technieken om gevendoriseerde code te detecteren, en het werkt goed in de eenvoudige gevallen. Als een ontwikkelaar een ongewijzigde kopie van een zelfstandige bibliotheek heeft gevendord, kun je de bestanden hashen, vergelijken met een corpus van bekende releases, en vaak een betrouwbare match krijgen. Voor kleine, op zichzelf staande bibliotheken kan dit behoorlijk betrouwbaar zijn.
Maar vendoring brengt complicaties met zich mee die van fingerprinting een heuristisch spel maken in plaats van een deterministisch spel.
Wijzigingen komen vaak voor. Ontwikkelaars vendoren code en passen die vervolgens aan — bugs repareren, API's aanpassen, ongebruikte functionaliteit verwijderen. Zodra een bestand is gewijzigd, werkt exacte hash-matching niet meer. Dan kom je terecht in fuzzy matching: vergelijken met bekende versies, technieken zoals MinHash of Winnowing gebruiken voor code-overeenkomst, of een structurele vergelijking op AST-niveau uitvoeren. Deze benaderingen kunnen werken, maar ze introduceren onzekerheid. Is dit een aangepaste kopie van FreeRTOS 10.4.3, of is het FreeRTOS 10.5.1 met andere wijzigingen? Het antwoord is van belang voor CVE-correlatie, en de tooling kan je dat vaak niet met voldoende zekerheid vertellen.
Vendor-SDK's voegen ruis toe. Halfgeleiderleveranciers zoals ST, NXP, TI en Renesas leveren SDK's mee die open-sourcecomponenten bundelen — FreeRTOS, lwIP, mbedTLS, FatFS — naast propriëtaire HAL-code. Deze bundels voegen vaak vendorspecifieke copyrightkopteksten toe, wijzigen directorystructuren, hernoemen bestanden, en forken de upstreamcode soms aanzienlijk. STM32CubeF4 bevat een versie van FreeRTOS die door ST's aanpassingsproces is gegaan. Renesas FSP bundelt een afgeleide van ThreadX. Deze tegen upstreamreleases fingerprinten is onbetrouwbaar zonder vendorspecifieke detectielogica.
Inbeddingsmodellen en op ML gebaseerde code-overeenkomst kunnen helpen, en er is interessant academisch werk over het gebruik van geleerde coderepresentaties voor identificatie van softwarecomponenten. Maar vandaag zijn deze benaderingen meestal te operationeel zwaar voor routinematige SBOM-generatie in CI op schaal. Ze kunnen een rol spelen bij eenmalige audits of onderzoeken met hoge zekerheidseisen, maar zijn nog niet een eenvoudige vervanging voor eenvoudigere technieken.
Het probleem met de gedeelde leveranciersmap
Er is een praktische complicatie die ik in SBOM-gesprekken niet vaak genoeg besproken zie. De meeste C/C++-ontwikkelaars bewaren hun vendored code in aparte mappen — vendor/, 3rdparty/, external/, lib/ — wat het beheer eenvoudiger maakt. Dat is goede hygiëne en het helpt SBOM-tools de grens tussen eigen code en code van derden te identificeren.
Maar in de praktijk, vooral in embedded-omgevingen, wordt een vendored map vaak gedeeld door meerdere projecten of build-targets. Een vendor/-map kan FreeRTOS, lwIP, mbedTLS, FatFS en een dozijn andere bibliotheken bevatten. Project A gebruikt FreeRTOS en lwIP. Project B gebruikt FreeRTOS en mbedTLS. Project C gebruikt ze allemaal.
Een SBOM-generator die simpelweg de hele vendor-map inventariseert, zal een SBOM opleveren die alle vendored componenten opsomt — maar die SBOM is onjuist voor elk afzonderlijk buildartefact. Hij overdrijft wat er daadwerkelijk in de binaire staat. Als Project A mbedTLS niet gebruikt, leidt het opnemen ervan in de SBOM van Project A tot false positives in kwetsbaarheidsscans en geeft het een vertekend beeld van het werkelijke aanvalsoppervlak.
In veel embedded projecten is de meest betrouwbare manier om dit goed te doen te begrijpen wat het buildsysteem daadwerkelijk compileert en linkt voor een gegeven target. Daarmee komen we terug bij het buildsysteem als een belangrijke bron van waarheid, en waarom analyse tijdens het builden meestal essentieel is voor embedded C/C++.
De echte wereld is rommelig: een gecombineerde aanpak
Geen enkele techniek lost het C/C++ SBOM-probleem op. Na het uitwerken van de verschillende benaderingen — manifestanalyse, inspectie van linkerflags, vingerafdrukken van bestanden, binaire analyse, instrumentatie van het buildsysteem — ben ik ervan overtuigd geraakt dat een gecombineerde aanpak de enige weg is naar een redelijk nauwkeurige SBOM. Concreet moet je vier signalen combineren:
Wat er gebouwd wordt. Het buildsysteem — of dat nu Make, CMake, Meson, IAR of iets anders is — weet welke bronbestanden worden gecompileerd en welke bibliotheken voor een gegeven target worden gelinkt. Door het buildproces te instrumenteren krijg je de feitelijke waarheid over wat in de binaire wordt opgenomen. Dit is de basis.
Wat er is meegeleverd. Directory fingerprinting, bestands-hashing en extractie van versiestrings kunnen bekende open-sourcecomponenten in vendormappen identificeren. De sleutel is om dit te correleren met wat het buildsysteem daadwerkelijk gebruikt — niet alleen met wat er op schijf aanwezig is. Een component in vendor/ die nooit in je target wordt gecompileerd, hoort niet in je SBOM thuis.
Voor welk platform of welke microcontroller het is gebouwd. Het doelplatform bepaalt welke vendor SDK's en HAL-bibliotheken in beeld zijn. Als je voor een STM32F4 bouwt, weet je dat STM32CubeF4 HAL-componenten waarschijnlijk aanwezig zijn. Als je je richt op een Renesas RA6M4, kijk je naar Renesas FSP. Platformbewustzijn helpt je de zoekruimte te verkleinen en vendorspecifieke detectieheuristieken toe te passen. Het helpt ook bij CPE/PURL-mapping, omdat de naamgeving van embedded componenten sterk vendorspecifiek is.
Statisch afhankelijkheidsbeheer. Inzicht in hoe statische bibliotheken (.a, .lib) worden gelinkt, welke objectbestanden ze bevatten en waar ze vandaan komen, maakt het beeld compleet. Mapbestanden (.map), linkerscripts en de inhoud van archieven geven allemaal aanwijzingen. Voor IAR-projecten vermelden .map-bestanden elk objectbestand dat in het eindimage wordt gelinkt. Voor builds op basis van GCC onthult ar -t op statische archieven de inhoud.
Elk van deze signalen afzonderlijk is onvolledig. Samen kunnen ze je naar een nauwkeurigere SBOM brengen, en dat is meestal wat telt voor kwetsbaarheidsbeheer, inkoop en rapportage aan toezichthouders.
Ontwikkelaarscontext blijft nog steeds belangrijk
Geautomatiseerde detectie is belangrijk, maar voor C/C++-projecten, vooral embedded projecten, blijft door ontwikkelaars aangeleverde context erg belangrijk. Het ontwikkelingsteam weet vaak welke meegeleverde bibliotheek is overgenomen, waar die vandaan komt, welke patches zijn toegepast en welke componenten bij welke buildvariant horen. Die context is precies het soort informatie dat geautomatiseerde scanners moeilijk betrouwbaar kunnen reconstrueren uit broncodebomen, archieven en gestript binaire bestanden.
Dat betekent niet dat handmatige of door ontwikkelaars onderhouden manifesten een wondermiddel zijn. Ze kunnen verouderen, afhankelijkheden missen of afwijken van de werkelijkheid als ze niet nauw aan het bouwproces zijn gekoppeld. In de praktijk kunnen ze echter een nuttige aanvulling zijn op geautomatiseerde analyse, vooral voor herkomst-, leverancier-, licentie- en buildvariantmetadata die achteraf moeilijk af te leiden zijn.
Gestructureerde componentmetadata is de ontbrekende laag in SBOM's — en Interlynk helpt deze te definiëren. Met de nieuwe sbom-generatiefunctie van sbomasm stellen we teams in staat ontwikkelaarscontext vast te leggen waar die ontstaat, waardoor geautomatiseerde scans en analyse tijdens de build aanzienlijk betrouwbaarder worden.
Waar gaan we vanaf hier naartoe?
Het C/C++ SBOM-probleem zal niet worden opgelost door één enkel hulpmiddel of één enkele techniek. Er is een gelaagde aanpak nodig die kennis van het buildsysteem, detectie van meegeleverde code, platformbewuste heuristieken en door de gemeenschap onderhouden corpora van bekende ingebedde bibliotheken en hun vingerafdrukken combineert.
De ingebedde C/C++-gemeenschap moet ook investeren in betere afhankelijkheidsdiscipline — package managers gebruiken waar dat haalbaar is, expliciete manifesten bijhouden voor meegeleverde code, en het genereren van SBOM's behandelen als een volwaardig build-artifact in plaats van als een bijzaak.
Voor degenen onder ons die tooling in dit domein bouwen, is de kans duidelijk: wie op betrouwbare wijze de kloof kan overbruggen tussen de rommeligheid van C/C++-projecten uit de praktijk en de gestructureerde, nauwkeurige SBOM's die regelgeving vereist, lost een van de moeilijkste resterende problemen in software supply-chainbeveiliging op.
En dat wordt een SBOM die u kunt vertrouwen en verifiëren.
Citaties
CMake, "FetchContent," officiële documentatie: https://cmake.org/cmake/help/latest/module/FetchContent.html
NTIA, "De minimale elementen voor een software bill of materials (SBOM)," juli 2021: https://www.ntia.gov/sites/default/files/publications/sbom_minimum_elements_report_0.pdf
Europese Commissie, "Cyber Resilience Act - implementatie," geraadpleegd op 9 april 2026: https://digital-strategy.ec.europa.eu/en/factpages/cyber-resilience-act-implementation
FDA, "Medical Device Software Guidance Navigator," inclusief "Cybersecurity in Medical Devices: Overwegingen voor het kwaliteitssysteem en inhoud van premarket-indieningen": https://www.fda.gov/medical-devices/regulatory-accelerator/medical-device-software-guidance-navigator
Het Witte Huis, "Executive Order on Improving the Nation's Cybersecurity" (Executive Order 14028), 12 mei 2021: https://www.whitehouse.gov/briefing-room/presidential-actions/2021/05/12/executive-order-on-improving-the-nations-cybersecurity/
sbomasm, "Uw SBOM-samensteller" : https://github.com/interlynk-io/sbomasm