How to Audit a Device for GPL Compliance: A Technical Guide

If you’ve ever wondered whether the Linux-powered gadget in your hands actually respects the GPL, you’re not alone. I’ve spent years tearing into embedded systems, and I can tell you that a GPL audit isn’t about legal posturing—it’s a hands-on forensic dig through firmware, build systems, and supply chains. This guide walks you through the exact steps I follow to see if a device truly honors the freedoms its software license demands.

Understanding the Scope of GPL Obligations

Before you fire up a terminal, you need to know what you’re hunting for. The GPL covers any work that includes or links to GPL-licensed code. In the embedded space, that usually means the Linux kernel, U-Boot, BusyBox, and a handful of userspace libraries. The license says the distributor has to hand over “the complete corresponding source code” for every GPL-covered binary on the device. That includes the scripts that control compilation and installation. A tarball of the upstream Linux kernel sources won’t cut it if the vendor patched the code, tweaked the kernel configuration, or linked proprietary kernel modules against GPL-only symbols.

My audits zero in on three layers: the bootloader, the operating system kernel, and the root filesystem. Each one has its own traps. Vendors often release kernel source that compiles but leaves out the exact .config they used for the shipped binary. Others hand over root filesystem source but strip the build scripts that show how proprietary bits were linked against GPL libraries. A real audit confirms that the provided source can be built into a binary that matches, bit for bit, what’s running on the hardware.

Step 1: Extract the Firmware and Identify Components

Grab the firmware image first. It might come from an over-the-air update, a download on the vendor’s site, or directly off the device’s flash memory through a hardware interface. Use binwalk to scan the image for known file signatures and filesystem offsets. A typical command is:

binwalk -e firmware.bin

This pulls out any recognizable filesystems, compressed archives, and kernels. Poke through the extracted directory structure. You should spot a Linux kernel image (often zImage or uImage), a root filesystem (SquashFS, JFFS2, or ext4), and maybe a separate bootloader partition. Run file on each extracted blob to confirm its type. For the kernel, use strings to hunt for the Linux version string—it spills the exact kernel release and the compiler version used. That detail becomes a big deal later when you try to reproduce the build.

For the root filesystem, mount it or browse it with unsquashfs. List all installed packages if a package manager is present. On embedded systems, you’ll often find BusyBox. Run busybox on the target or in a QEMU chroot to see the applets and their features. Note any proprietary binaries in /usr/bin or /opt. These are the programs you need to check for dynamic linking against GPL libraries. Use readelf -d on each binary to list shared library dependencies. If a proprietary binary links to libc.so.6 (glibc, LGPL) or libpthread.so.0, the vendor has to provide the exact source and build scripts for those libraries, or offer a way to relink.

Close-up of a circuit board with multiple chips and traces, representing the hardware layer of an embedded device under audit.

Step 2: Request and Examine the Provided Source

Most vendors stick a source download link in the device’s docs or on a support page. Download the archive and compare its guts against the firmware components you already identified. The archive should have a top-level directory for each GPL-licensed component, like linux-3.18.29 or busybox-1.24.2. Inside each, you need to find the exact .config file used for the build. For the Linux kernel, the configuration decides which drivers get compiled, whether loadable module support is on, and which security features are active. Without the matching config, you can’t produce a kernel that behaves the same as the shipped one.

Check for patches. Vendors often modify upstream source to add board support, fix bugs, or integrate proprietary hardware abstraction layers. The GPL says these modifications must be distributed in the preferred form for making changes—usually as patch files against a known upstream release. Look for a patches/ directory or a series file. Apply the patches to the corresponding upstream tag and verify that the resulting tree matches the vendor’s source tree using diff -rq. If the vendor ships a single tarball of modified source without separating patches, it’s technically compliant but makes verification a headache. In that case, diff the entire tree against the upstream release to understand the changes.

Verifying Build Scripts and Toolchains

The GPL requires “scripts used to control compilation and installation.” That means the vendor has to provide the exact build commands, cross-compiler configuration, and any wrapper scripts. Look for a top-level Makefile or a build.sh that sets environment variables like CROSS_COMPILE, ARCH, and INSTALL_MOD_PATH. The toolchain itself isn’t required unless it contains GPL components, but the vendor must specify the exact toolchain version and source if it’s needed to produce a working binary. I’ve run into cases where a vendor used a heavily patched GCC that quietly worked around silicon bugs; without that toolchain source, the provided kernel source was useless.

Reproduce the build in a clean environment. Use the same toolchain version, apply the patches, copy the .config, and run the build commands. Compare the resulting binary against the extracted firmware binary. For the kernel, compare the uncompressed vmlinux images using sha256sum. For the root filesystem, build each component and compare individual binaries. A mismatch often points to missing patches, a different toolchain, or build timestamps embedded in the binary. Timestamps are a known headache; the GPL doesn’t demand bit-identical builds, but you should be able to produce a functionally equivalent binary. If the vendor’s build system strips debug symbols or runs strip with different flags, the binaries will differ. Document these differences and assess whether they affect functionality.

A developer's desk with a laptop showing terminal output, a notebook, and a smartphone, illustrating the software analysis phase of a compliance audit.

Step 3: Analyze Dynamic Linking and Derivative Works

The messiest corner of GPL compliance is the boundary between proprietary userspace applications and GPL libraries. If a proprietary program links dynamically to a GPL library, the whole work counts as a derivative and must be licensed under the GPL. But plenty of vendors lean on the system library exception or argue that linking to LGPL libraries doesn’t trigger the GPL. As an auditor, you have to map the exact linking relationships.

On the extracted root filesystem, run ldd on every proprietary binary. Note which libraries are GPL-licensed. For example, if a binary links to libgcc_s.so.1, which is covered by the GCC Runtime Library Exception, the situation is different from linking to libreadline.so, which is pure GPL. Check the license of each shared library by looking for copyright files in the root filesystem or by identifying the source package. If a proprietary binary links to a GPL library, the vendor must either release the binary’s source under GPL or provide a written offer to do so. If they refuse, the device is non-compliant.

Kernel modules are another flashpoint. Proprietary kernel modules that use only exported GPL symbols are generally considered derivative works of the kernel. Check the module’s license tag with modinfo. A module that declares license=Proprietary but uses GPL-only symbols is a red flag. Boot the device and check /proc/kallsyms for symbols marked with GPL_ONLY. If the proprietary module references any of these, the vendor has to provide the module’s source code. Many vendors try to dodge this with a shim module that re-exports symbols under a non-GPL license; this practice has been repeatedly rejected by kernel developers and is legally shaky.

Step 4: Check for Complete Installation Information

The GPL requires that the source include any scripts or data used to install the firmware on the device. This gets overlooked all the time. If the device uses a signed bootloader that only accepts firmware images encrypted with a vendor key, does the vendor have to hand over the signing tools and keys? No—the GPL doesn’t require release of private keys, but it does require that the user be able to exercise their freedom to modify and run the software on the device. If the device refuses to boot a modified kernel because of signature checks, the vendor is effectively blocking GPL freedoms. That’s a violation unless the vendor provides a way to disable the signature check or to sign images with a user-controlled key.

Examine the bootloader environment. Dump the U-Boot environment variables using fw_printenv if available. Look for bootargs, bootcmd, and any variables that reference signature verification. If the bootloader is locked, you may need to attach a serial console and interrupt the boot process. Document whether the bootloader allows booting an unsigned kernel or initramfs. If not, the device is likely non-compliant, regardless of the source code provided.

A person using a multimeter to probe a circuit board, representing the hardware-level verification in a GPL audit.

Step 5: Document Findings and Engage the Vendor

Compile a detailed report of your findings. For each GPL component, list the expected source, the provided source, and any discrepancies. Include SHA-256 hashes of the shipped binaries and the binaries you built from the provided source. Note any missing build scripts, config files, or patches. If you found proprietary binaries linking to GPL libraries, list the exact library names and the symbols used. This technical report serves as the basis for a compliance request.

Send the report to the vendor’s designated GPL compliance contact. The GPL requires that this contact be disclosed in the product documentation or on the vendor’s website. If no contact is listed, send the request to the general support address and escalate as needed. Be specific about what is missing and what the vendor must provide to become compliant. Set a reasonable deadline—30 days is standard. Many vendors will respond with a complete source archive once they understand the gaps. Others will stall or ignore the request. In those cases, the report becomes evidence for enforcement actions by copyright holders.

Common Pitfalls and How to Spot Them

Over the years, I’ve catalogued the most frequent compliance failures. Incomplete kernel source is the most common: vendors ship a vanilla kernel tarball without their board-specific patches. You can spot this by diffing the provided source against the upstream release and finding no changes, yet the device’s kernel has custom drivers visible in /proc/config.gz or dmesg. Missing BusyBox source is another classic. Vendors often forget that BusyBox is GPL, not LGPL, and that its applets are statically linked into a single binary. If the device runs BusyBox and the source archive contains no BusyBox directory, it’s non-compliant.

Obfuscated build systems are a more subtle problem. Some vendors provide source that compiles but requires a specific directory structure, environment variables, or pre-built binary toolchains that aren’t documented. The build script might reference paths like /home/jenkins/workspace/project_x that don’t exist on your machine. This violates the requirement that the source be in the “preferred form for making modifications.” A compliant build system is self-contained and documented. If you can’t build the firmware after reasonable effort, the vendor hasn’t met their obligations.

FAQ

What if the vendor provides source only on a physical medium like a CD?

The GPL allows a written offer to provide source on a physical medium, but the offer must be valid for at least three years and the cost must be no more than the cost of physically performing the distribution. If the vendor refuses to provide a download link and insists on a CD, they must ship it promptly upon request. In practice, a download link is the preferred method and any vendor that refuses to provide one is often hiding incomplete source.

How do I verify that the kernel source matches the running kernel exactly?

Extract the kernel image from the firmware and look for the version string using strings. The string includes the kernel version, the compiler version, and the user who built it. Build the provided source with the same compiler and compare the resulting vmlinux binary. If the vendor applied patches, the version string should include a custom suffix. Check /proc/version on the running device for the exact string. If the strings don’t match, the source is incomplete.

What should I do if a device uses a locked bootloader and the vendor refuses to provide signing keys?

A locked bootloader that prevents running modified software is a GPL violation because it denies the user the freedom to run their modified versions of the GPL-covered code. Document the bootloader behavior, the vendor’s refusal, and report the violation to the copyright holders of the GPL components involved—typically the Linux kernel developers or the Software Freedom Conservancy. They have legal standing to enforce the license.

Is a vendor required to provide the source for their proprietary applications if they only use LGPL libraries?

No, the LGPL allows proprietary applications to link dynamically to LGPL libraries without requiring the application’s source to be released. However, the vendor must still provide the source for the LGPL libraries themselves, along with any modifications they made, and must allow the user to relink the application against a modified version of the library. Check that the vendor provides the exact library source and build scripts, and that the application is dynamically linked, not statically linked.