C SBOMs, and how pkgconf can solve this problem

I recently attended FOSDEM, and saw a talk in the SBOM devroom about a software engineer’s attempts to build an SBOM for a C project. There are a number of reasons why the C ecosystem is difficult to reflect in SBOMs, but the largest problem is that the C ecosystem is fractured across a handful of build systems: GNU Autotools, CMake and Meson are the primary build systems used by projects but there are hundreds of others in the long tail.

A key thing that these build systems have in common is that they can integrate with pkg-config, which is a database that describes available build dependencies and their use. This database, naturally, is of significant relevance to SBOM generation, because it already has most of the relevant information needed to generate an SBOM.

pkgconf has a bomtool utility which is intended to generate SBOMs using pkg-config data. But how can this be leveraged in practice?

To show how bomtool can be used to generate SBOMs, lets make a simple project using Meson. First, we need a simple program:

main.c

#include <glib.h>

int main(int argc, const char *argv[])
{
        g_print("hello world\n");
        return 0;
}

Now we can compile this program by hand:

~/bomtool-example $ gcc -o main main.c `pkg-config --cflags --libs glib-2.0`
~/bomtool-example $ ./main
hello world

meson.build

Now we can generate a Meson project to build this program:

project('bomtool-example', 'c')

glib = dependency('glib-2.0')

exe = executable('bomtool-example', 'main.c', dependencies: [glib])

At that point, you can run meson setup _build && cd _build && ninja and get a bomtool-example binary that effectively matches the one built by hand earlier.

How do we turn that into an SBOM though? Well, we need to extend the Meson build script to generate a pkg-config module, which we can do by adding:

pkg = import('pkgconfig')
pcfile = pkg.generate(name: 'bomtool-example', filebase: 'bomtool-example', description: 'bomtool example', version: '0.0.1', requires: [glib])

This causes the following .pc file to be generated:

prefix=/usr/local
includedir=${prefix}/include

Name: bomtool-example
Description: bomtool example
Version: 0.0.1
Requires: glib-2.0
Cflags: -I${includedir}

Now we can use bomtool to generate an SBOM:

~/bomtool-example/build $ bomtool ./meson-private/bomtool-example.pc > bomtool-example.spdx.txt
~/bomtool-example/build $ tail -n 10 bomtool-example.spdx.txt 
Relationship: SPDXRef-Package-glib-2.0C642.82.4 DEPENDENCY_OF SPDXRef-Package-bomtool-exampleC640.0.1


Relationship: SPDXRef-Package-glib-2.0C642.82.4 DEPENDS_ON SPDXRef-Package-libpcre2-8C6410.43
Relationship: SPDXRef-Package-libpcre2-8C6410.43 DEV_DEPENDENCY_OF SPDXRef-Package-glib-2.0C642.82.4


Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package-bomtool-exampleC640.0.1
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package-glib-2.0C642.82.4
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package-libpcre2-8C6410.43

What is left to do? A few things:

  • Build systems like CMake and Meson should become aware of bomtool and leverage it automatically.

  • pkg-config module authors should add SPDX license expressions to their pkg-config modules, support for this was added in pkgconf 1.9, so it is a reasonably stable feature now. This will improve the quality of the SBOMs generated by bomtool.

  • Support for output formats that are more useful such as the new SPDX 3 JSON-LD format, CycloneDX, etc. Tools exist today which allow for translation between these formats, however, so it is not a huge requirement.

It is pretty clear, at least to me that the pkg-config ecosystem has a large role to play in the future of C SBOMs, as the necessary information about dependencies and other relationships are richly expressed at this layer. But at the same time, bomtool is still new, and .pc files are still being updated to reflect their projects' license data.