Using Buildroot to Customize the Toolchain and Applications
- 1 1. Overview
- 2 2. BSP Distribution Structure
- 3 3. Buildroot Configurations
- 4 4. Kernel Headers Integration
- 5 5. Building the Toolchain from Buildroot
- 6 6. Configuring a BSP Project to Use Buildroot
- 7 7. Adding Packages in Buildroot
- 8 8. Busybox Integration
- 9 9. Available Build Targets
- 10 10. Saving Buildroot Configuration Changes
1. Overview
The Emcraft uClinux BSP includes Buildroot -- a build system that generates a complete cross-compilation toolchain, root filesystem, and user-space packages for the ARM Cortex-M7 targets. The BSP ships with a pre-compiled toolchain in the tools/ directory, which is sufficient for most use cases. However, advanced users may need to:
Rebuild the toolchain with different configuration options.
Add third-party packages (e.g., networking utilities, scripting languages, additional libraries) to the root filesystem.
Customize the C library (uClibc-ng) or compiler (GCC) settings.
The Buildroot source tree is provided in the buildroot/ directory at the top level of the BSP distribution. Each BSP project can be independently configured to use either the pre-compiled toolchain from tools/ or a custom toolchain built from Buildroot.
This application note covers:
The Buildroot directory structure and default configuration.
How the Buildroot toolchain uses kernel headers from the BSP's Linux tree.
How to build the toolchain and SDK from Buildroot.
How to configure a BSP project to use the Buildroot-built toolchain.
How to add packages and include them in the root filesystem.
2. BSP Distribution Structure
The BSP top-level directory contains the following components relevant to Buildroot:
linux-cm-imxrt117x-3.3.2/
buildroot/ Buildroot source tree
tools/ Pre-compiled toolchains
projects/ BSP projects (rootfs, rootfs_flash, etc.)
linux/ Linux kernel source
u-boot/ U-Boot bootloader source
A2F/ Third-party libraries (busybox, LVGL, etc.)
ACTIVATE.sh Environment setup scriptA single cross-compilation toolchain -- arm-buildroot-uclinuxfdpiceabi- (uClibc-ng FDPIC) -- drives the entire build: the Linux kernel, U-Boot, and userspace applications. Bare-metal targets (kernel, U-Boot armv7m) suppress the toolchain's FDPIC/PIE defaults with -mno-fdpic -fno-pic -no-pie; those flags are baked into the kernel's arch/arm/Makefile and U-Boot's arch/arm/cpu/armv7m/config.mk.
The pre-compiled toolchain in tools/ is a snapshot of the Buildroot SDK output. The buildroot/ directory contains the full source to rebuild it.
3. Buildroot Configurations
The Buildroot source tree includes two pre-defined configurations for the BSP:
Defconfig | Description |
|---|---|
| Lightweight SDK – toolchain and the packages the stock |
| Full SDK – the complete package set tested by Emcraft on the noMMU/FDPIC toolchain; also the source from which the |
The arm_cortexm_sdk_lite_defconfig is recommended when you only need what the stock rootfs project deploys: busybox, GDB, i2c-tools, libubootenv, tslib, dropbear, and openssh-sftp-server. Anything beyond this set will be built but not included in the on-target image, only inflating the SDK.
The full arm_cortexm_sdk_defconfig is the complete package set Emcraft tests against the noMMU/FDPIC toolchain. It extends the lite set with libostree, aktualizr (Uptane OTA client), their dependencies (boost, libarchive, libsodium, sqlite, jsoncpp, libtirpc, openssl, libcurl, expat, util-linux, host-asn1c, urandom-scripts, bzip2, jq) – supporting an OSTree-based root filesystem with Uptane-compliant OTA updates as a primary feature. The full defconfig also serves a second purpose: its target/ tree is the source from which A2F/root is regenerated, so projects that opt out of Buildroot mode (see §6.2) still see a layout-compatible userspace.
Both configurations produce a toolchain with the following settings:
Architecture: ARM (32-bit), ARMv7-M (Cortex-M7)
Instruction set: Thumb-2 only
FPU: FPv4-D16
Binary format: FDPIC (position-independent executables for no-MMU)
C library: uClibc-ng 1.0.47 with FDPIC support
Compiler: GCC 11.3.0 with C++ support
Threading: NPTL
The Buildroot source tree also includes Emcraft-specific patches to several packages for no-MMU compatibility, including fork() to vfork() substitutions in e2fsprogs, libglib2, and others.
4. Kernel Headers Integration
The Buildroot toolchain requires Linux kernel headers to build the C library (uClibc-ng) and certain packages. Rather than downloading a separate copy of the kernel source, the BSP build system configures Buildroot to use the kernel headers directly from the BSP's linux/ directory.
This is implemented using Buildroot's LINUX_HEADERS_OVERRIDE_SRCDIR mechanism. When a project build is triggered, the build system automatically generates a local.mk file in the Buildroot output directory:
LINUX_HEADERS_OVERRIDE_SRCDIR = <BSP_ROOT>/linuxThis tells Buildroot to skip downloading kernel source and instead use the local kernel tree. Buildroot rsyncs the source into its build directory and runs make headers_install to extract the sanitized userspace headers. The toolchain is then built against these headers, ensuring that the userspace headers always match the kernel version shipped with the BSP (v6.12.20).
This process is fully automatic – no manual configuration is required. The local.mk file is created before the first Buildroot build and does not need to be regenerated unless the Buildroot output directory is cleaned.
5. Building the Toolchain from Buildroot
5.1. Prerequisites
Source the BSP activation script. The selection of a Buildroot SDK is made here, as the second positional argument:
$ cd linux-cm-imxrt117x-3.3.2
$ source ACTIVATE.sh <MCU> <buildroot_defconfig>For example:
$ source ACTIVATE.sh IMXRT117X_NXPEVK arm_cortexm_sdk_lite_defconfigWhen the second argument is given, ACTIVATE.sh exports BUILDROOT_DEFCONFIG and points CROSS_COMPILE, CROSS_COMPILE_APPS, TOOLS_DIR, TOOLS_LIBS, and PATH at the Buildroot output (buildroot/output_<defconfig-stem>/host/). Sourcing ACTIVATE.sh without the second argument selects legacy mode (pre-compiled toolchain from tools/).
The resolved selection is persisted to .activate-defaults at the BSP root, so the next no-argument source ACTIVATE.sh re-applies the same MCU and Buildroot SDK without retyping them. See Installing and Activating Cross Development Environment for full details on activation.
5.2. Automatic Build
With a Buildroot SDK selected via ACTIVATE.sh, a normal make in any BSP project builds the SDK on demand. No project-Makefile edit is required.
$ source ACTIVATE.sh IMXRT117X_NXPEVK arm_cortexm_sdk_lite_defconfig
$ cd projects/rootfs
$ makeIf the Buildroot toolchain has not been built yet, the build system detects this automatically and builds it before proceeding with the kernel and application build. The first build takes a significant amount of time depending on the host machine, as it compiles GCC, binutils, uClibc-ng, and all selected packages from source. Subsequent builds reuse the existing SDK; Buildroot's per-package stamps drive any incremental rebuilds.
5.3. Explicit Buildroot Build
To build the Buildroot toolchain explicitly (e.g., to trigger a rebuild or generate the SDK tarball), use:
$ source ACTIVATE.sh IMXRT117X_NXPEVK arm_cortexm_sdk_lite_defconfig
$ cd projects/rootfs
$ make buildrootThis command performs three steps:
make $BUILDROOT_DEFCONFIG-- loads the Buildroot configuration selected viaACTIVATE.sh.make-- builds the complete toolchain, C library, and all enabled packages.make sdk-- packages the toolchain into a relocatable SDK tarball.
Each Buildroot defconfig gets its own output directory, derived from the defconfig name. For example:
Defconfig | Output directory |
|---|---|
|
|
|
|
Multiple SDK builds coexist on disk under their per-defconfig output directories. Switching SDKs is a matter of re-sourcing ACTIVATE.sh with a different second argument -- the previously built SDK stays around and the new one builds on first make.
5.4. Building Directly in the Buildroot Directory
Alternatively, you can build the toolchain directly in the Buildroot directory. Use the O= parameter to specify the output directory:
$ cd buildroot
$ make O=output_arm_cortexm_sdk_lite arm_cortexm_sdk_lite_defconfig
$ make O=output_arm_cortexm_sdk_lite
$ make O=output_arm_cortexm_sdk_lite sdkThis is useful if you want to configure and build the toolchain before associating it with a specific BSP project. The O= directory name should match the convention used by the BSP build system: output_ followed by the defconfig name without the _defconfig suffix.
6. Configuring a BSP Project to Use Buildroot
6.1. Enabling the Buildroot Toolchain
To use the Buildroot-built toolchain for a project, source ACTIVATE.sh with the desired Buildroot defconfig as the second positional argument:
$ source ACTIVATE.sh <MCU> <buildroot_defconfig>For example:
$ source ACTIVATE.sh IMXRT117X_NXPEVK arm_cortexm_sdk_lite_defconfigThe selection is persisted in .activate-defaults at the BSP root, so subsequent no-argument source ACTIVATE.sh invocations reproduce it. Project Makefiles carry no toolchain-selection logic of their own and the same project builds against any selected SDK without edits.
When BUILDROOT_DEFCONFIG is set in the environment (by ACTIVATE.sh), the build system points the following variables at the corresponding Buildroot output (e.g. output_arm_cortexm_sdk_lite for arm_cortexm_sdk_lite_defconfig):
Variable | Value |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Prepended with |
| Exported as-is so |
All custom application Makefiles that use $(CROSS_COMPILE_APPS)gcc (or $(CROSS_COMPILE)gcc) will automatically pick up the Buildroot-built compiler.
6.2. Reverting to the Pre-compiled Toolchain
Source ACTIVATE.sh with only the MCU argument (and no Buildroot defconfig):
$ source ACTIVATE.sh IMXRT117X_NXPEVKACTIVATE.sh rewrites .activate-defaults to drop any persisted BUILDROOT_DEFCONFIG, unexports the variable from the current shell, and points CROSS_COMPILE, PATH, TOOLS_DIR, etc. at the toolchain in tools/. In this mode Rules.make exports TARGET_ROOT := $(INSTALL_ROOT)/A2F/root, so any .initramfs line written against ${TARGET_ROOT} continues to resolve to a working source – A2F/root mirrors the layout (sonames, directory structure, ABI) of the Buildroot target/ tree produced by the full arm_cortexm_sdk_defconfig (see §3). Packages not yet migrated to Buildroot (pppd, wpa_supplicant, libnl, bluetooth/obex, alsa, etc.) live in the same A2F/root tree and remain available regardless of mode.
6.3. Switching SDKs in an Already-Built Project
When a project is rebuilt under a different SDK selection than its previous build, leftover artefacts (kernel modules, custom-app objects, the assembled uImage) are stale – they were compiled against the previous sysroot. Linking them with a different sysroot's libraries silently produces a mixed image.
To catch this, each project records the active SDK in a .sdk_stamp file in the project directory (one line: legacy or buildroot:<defconfig>). On the next make, if the stamp disagrees with the currently selected SDK, the build hard-fails with a diagnostic and refuses to proceed:
ERROR: rootfs was last built against SDK 'buildroot:arm_cortexm_sdk_lite_defconfig',
current activation selects SDK 'buildroot:arm_cortexm_sdk_defconfig'.
Run 'make clean' before rebuilding.Run make clean to clear the project's outputs (including the stamp) and re-make to build fresh against the new SDK. The Buildroot output directory is not cleaned -- previously built SDKs stay on disk and are reused if you switch back later.
7. Adding Packages in Buildroot
7.1. Selecting Packages
Buildroot includes over 2600 packages. To browse and select packages, use menuconfig from the project directory:
$ cd projects/rootfs
$ make buildroot-menuconfigThis opens the Buildroot interactive configuration menu. Navigate to Target packages to browse available packages organized by category (audio, networking, libraries, etc.). Select the desired packages, save the configuration, and exit. The changes are written back to the project's defconfig automatically -- there is no separate savedefconfig step.
The next make in the project directory automatically detects that the defconfig has changed (via md5 checksum) and rebuilds Buildroot to include the newly selected packages:
$ makeNote on package compatibility. Only the packages included in the Emcraft-maintained defconfigs (arm_cortexm_sdk_lite_defconfig and arm_cortexm_sdk_defconfig) have been verified to build and run on the noMMU/FDPIC toolchain. Buildroot itself contains more than 2600 packages; we do not provide a warranty that any particular one will work out of the box on this target.
Common reasons a package may fail include:
Hard dependency on an MMU (uses
fork()or functionality guarded byBR2_USE_MMUin itsConfig.in) -- such packages cannot be enabled inmenuconfigat all, since Buildroot hides them for noMMU targets.Autotools/CMake not recognizing the
uclinuxfdpiceabihost triple -- the package's ownconfigure.inoraclocal.m4may reject the target as "unsupported", disable shared-library support, or fall into a legacy code path.FDPIC ABI quirks -- some tools that rely on detailed ELF layout, ptrace register layouts, or dynamic-loader internals may build cleanly but misbehave at runtime.
Many additional packages do work without modification. If you need a package that is not in the maintained defconfigs, expect some integration effort. Contact Emcraft support if you hit a package you would like to see validated or patched upstream.
7.2. Including Buildroot Packages in the Root Filesystem
After building a package in Buildroot, its output binaries and libraries are placed in the Buildroot staging and target directories (paths shown for arm_cortexm_sdk_lite_defconfig):
Binaries:
buildroot/output_arm_cortexm_sdk_lite/target/usr/bin/,.../usr/sbin/Libraries:
buildroot/output_arm_cortexm_sdk_lite/target/usr/lib/Headers (for development):
buildroot/output_arm_cortexm_sdk_lite/host/arm-buildroot-uclinuxfdpiceabi/sysroot/usr/include/
To include these in a BSP project's root filesystem, add appropriate entries to the project's .initramfs configuration file. The build system exports ${TARGET_ROOT} -- a single source-prefix variable that resolves to the Buildroot target directory when BUILDROOT_DEFCONFIG is set, or to $(INSTALL_ROOT)/A2F/root when it is unset. Use ${TARGET_ROOT} in .initramfs files so the same entry works in both modes (${BUILDROOT_TARGET} is also exported in Buildroot mode as a legacy alias, but new entries should use ${TARGET_ROOT}).
For example, to include the nanocom serial terminal (after enabling BR2_PACKAGE_NANOCOM via make buildroot-menuconfig):
# In rootfs.initramfs -- add Buildroot binaries using the ${TARGET_ROOT} alias
dir /usr/bin 0755 0 0
file /usr/bin/nanocom ${TARGET_ROOT}/usr/bin/nanocom 0755 0 0If the chosen package depends on shared libraries built by Buildroot, include those too with additional file entries pointing at ${TARGET_ROOT}/usr/lib/.
For the rare case where a binary's path differs between Buildroot and legacy mode (the only current example is busybox: ${TARGET_ROOT}/bin/busybox in Buildroot mode versus $(INSTALL_ROOT)/A2F/busybox/busybox in legacy mode), use the ifdef/ifndef BUILDROOT_DEFCONFIG directives that rfs-builder.py evaluates against the build-time environment:
ifdef BUILDROOT_DEFCONFIG file /bin/busybox ${TARGET_ROOT}/bin/busybox 755 0 0
ifndef BUILDROOT_DEFCONFIG file /bin/busybox ${INSTALL_ROOT}/A2F/busybox/busybox 755 0 0The ${TARGET_ROOT} variable is resolved automatically by rfs-builder.py during the build; it is always defined, regardless of mode.
7.3. Linking Applications Against Buildroot Libraries
When BUILDROOT_DEFCONFIG is enabled, the Buildroot sysroot is exported via the TOOLS_LIBS variable. Application Makefiles can reference headers and libraries from this sysroot. For example, to link an application against a library built by Buildroot:
CC = $(CROSS_COMPILE_APPS)gcc
CFLAGS = -Os -I$(TOOLS_LIBS)/usr/include
LDFLAGS = -L$(TOOLS_LIBS)/usr/lib -lsomelib
app: app.c
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)8. Busybox Integration
When BUILDROOT_DEFCONFIG is set in the environment (by sourcing ACTIVATE.sh with a second argument), busybox is built through Buildroot's infrastructure instead of the standalone A2F/busybox build. The project's existing busybox configuration file (<sample>.busybox) is used automatically -- Buildroot applies it via the BUSYBOX_CONFIG_FILE override in local.mk.
The busybox source code is shared between both build paths. Buildroot uses the BSP's A2F/busybox/ source tree via the BUSYBOX_OVERRIDE_SRCDIR mechanism, so any Emcraft-specific patches in the busybox source are preserved.
The build system tracks changes to the busybox configuration using an md5 checksum. If the config file changes between builds, busybox is automatically rebuilt. A clean rebuild can be forced with make bclean.
The following busybox-related targets work in both modes (buildroot and legacy):
Target | Description |
|---|---|
| Open busybox interactive configuration |
| Clean busybox build |
The busybox binary is included in the root filesystem from ${BUILDROOT_TARGET}/bin/busybox when using buildroot mode.
9. Available Build Targets
The following Buildroot-related make targets are available in any BSP project when BUILDROOT_DEFCONFIG is set in the environment (sourced via ACTIVATE.sh's second argument):
Target | Description |
|---|---|
| Load defconfig, build everything, and generate the SDK |
| Open Buildroot interactive configuration; saves changes back to the defconfig on exit |
| Re-generate the SDK tarball (after manual changes) |
| Clean the Buildroot output directory |
| Open busybox interactive configuration (via Buildroot) |
| Clean busybox build (via Buildroot) |
10. Saving Buildroot Configuration Changes
Changes made via make buildroot-menuconfig are saved back to the project's defconfig automatically on exit (equivalent to running savedefconfig and copying the result into buildroot/configs/). No manual save step is required.
To preserve the changes as a separate named defconfig instead of modifying the active one, copy the current defconfig under a new name:
$ cp buildroot/configs/arm_cortexm_sdk_lite_defconfig buildroot/configs/my_custom_defconfigThen re-source ACTIVATE.sh with the new name as its second argument:
$ source ACTIVATE.sh IMXRT117X_NXPEVK my_custom_defconfigThe selection is persisted to .activate-defaults. Subsequent make buildroot-menuconfig invocations will modify my_custom_defconfig.