Every free embedded systems course eventually reaches the point where the stock root filesystem is not enough – you have your own binary, your own init script, or a config file that has to land in a specific place on the target. Buildroot’s answer to this is the root filesystem overlay: a plain directory tree that gets copied on top of the generated rootfs at the very end of the build. This lecture shows exactly how that mechanism works and where its limits are.
What You Will Learn
- What a Buildroot overlay is and precisely when it gets applied during the build
- How to compile a standalone program with the Buildroot toolchain so it runs correctly on target
- How to lay out an overlay directory and register it in the build configuration
- Where overlays stop being the right tool, and a real package becomes necessary instead
Prerequisites
- A working Buildroot configuration that already produces a booting
rootfsimage, covered in the previous lecture of this free linux device drivers course - Comfort with cross-compilation and the target sysroot, from earlier modules in this free linux kernel development course
What an Overlay Actually Is
A Buildroot overlay is nothing more than a directory on your host machine whose contents get recursively copied on top of the target root filesystem near the very end of the build, after every package has finished installing but before the final image (ext4, squashfs, tar, and so on) is packaged up. There is no special format, no manifest file, and no build step of its own – if a file exists in the overlay directory at the path bin/ep_monitor, it lands at exactly /bin/ep_monitor on the target.
This makes overlays the right tool for two very different jobs: dropping in a handful of files you already have (an init script, a config file, a certificate), or, combined with the toolchain, dropping in a compiled program you built separately from Buildroot’s own package system.
Compiling a Standalone Program for the Target
Before a compiled binary can go into an overlay, it has to be built with the same toolchain, and against the same C library, that Buildroot used for every other package on the target – mixing an ABI from your host’s native compiler with a target built by a different toolchain is a common source of mysterious crashes. Buildroot always builds its own toolchain under output/host, and the simplest way to use it outside of Buildroot’s own package system is to put it first on your PATH:
$ export PATH=$(pwd)/output/host/usr/bin:$PATH
$ ${'$'}{ARCH}-linux-gcc --version
Every cross tool Buildroot generated is prefixed with the target triplet, for example arm-linux-gnueabihf-gcc or aarch64-linux-gcc depending on your board. As a concrete example, here is a tiny original status-reporting utility – not the book’s demo program, just a small original tool for this lecture – that we will place on the target with an overlay:
/* ep_monitor.c - reports board uptime once, for overlay demo purposes */
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *f = fopen("/proc/uptime", "r");
if (!f) {
perror("ep_monitor: fopen");
return EXIT_FAILURE;
}
double uptime_seconds;
if (fscanf(f, "%lf", &uptime_seconds) != 1) {
fprintf(stderr, "ep_monitor: failed to read uptime\n");
fclose(f);
return EXIT_FAILURE;
}
fclose(f);
printf("ep_monitor: board has been up for %.0f seconds\n", uptime_seconds);
return EXIT_SUCCESS;
}
$ arm-linux-gnueabihf-gcc -O2 -o ep_monitor ep_monitor.c
$ file ep_monitor
ep_monitor: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked
Laying Out the Overlay Directory
The overlay directory mirrors the target’s own filesystem layout starting from the root. To ship ep_monitor in /usr/bin and start it automatically at boot with a BusyBox-style init script, the overlay tree looks like this:
$ mkdir -p board/epboard/overlay/usr/bin board/epboard/overlay/etc/init.d
$ cp ep_monitor board/epboard/overlay/usr/bin/
$ chmod 755 board/epboard/overlay/usr/bin/ep_monitor
#!/bin/sh
# board/epboard/overlay/etc/init.d/S60ep_monitor
case "$1" in
start)
/usr/bin/ep_monitor
;;
stop)
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
;;
esac
exit 0
Registering the Overlay
Buildroot needs to be told where the overlay directory lives, which you can do either through menuconfig or directly by hand in the option BR2_ROOTFS_OVERLAY:
This field accepts a space-separated list, so you can stack several overlays – a common pattern is one overlay shared across all boards for common files, and one board-specific overlay layered on top for hardware-specific configuration. Buildroot copies them in the order listed, and later entries win on any file path collision.
$ make
$ ls output/target/usr/bin/ep_monitor
output/target/usr/bin/ep_monitor
Two Details That Save Debugging Time
The permissions and ownership of every file placed in the root filesystem are ultimately controlled by system/skeleton for the base layout, and by device_table.txt style permission tables for anything that needs a specific UID, GID, or device node that a plain file copy cannot express – overlays alone cannot create device nodes or set non-default ownership, since a host-side directory copy has no concept of target UIDs beyond what your host filesystem happens to record.
Second, Buildroot does not rebuild an overlay’s contents – if you change ep_monitor.c and recompile it, you must recopy the binary into the overlay directory yourself before running make again, because the overlay step is a dumb copy, not a build rule with its own dependency tracking.
Common Mistakes and Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
| Program segfaults immediately on target | Binary built with the host’s native compiler instead of the Buildroot cross toolchain | Rebuild with the prefixed cross compiler from output/host/usr/bin |
| Init script never runs at boot | Script not executable, or missing the correct S<NN> prefix expected by the init system | chmod 755 the script and check the numeric ordering against other S* scripts |
| File appears to vanish after rebuild | A package installed later overwrote the same path | List your overlay after that package’s install step, or use a distinct path |
| Overlay changes seem to have no effect | Old binary was still sitting in the overlay directory | Recopy the freshly built binary before rerunning make |
Best Practices
- Keep overlays small – a handful of files and scripts, not entire application trees with their own build process
- Use a Buildroot package instead of an overlay once a program needs to be compiled as part of the Buildroot build itself, covered in the next lecture
- Separate a shared, board-independent overlay from a board-specific one so common configuration is not duplicated per board
- Never hand-edit files inside
output/targetdirectly – those changes are silently discarded on the next build
Summary and Key Takeaways
- An overlay is a plain directory copied on top of the target root filesystem at the end of the build, with no build logic of its own
- Anything compiled for an overlay must use Buildroot’s own cross toolchain to match the target ABI and C library
BR2_ROOTFS_OVERLAYaccepts multiple space-separated directories, applied in order
Conclusion
Overlays are the fastest path from “I have a file” to “that file is on my board,” which is exactly why they show up so early in almost every free linux device drivers course. They are intentionally simple, and that simplicity is also their boundary: the moment your own code needs its own build rules, dependency tracking, or version pinning inside the Buildroot tree, it is time to graduate from an overlay to a real Buildroot package, which is exactly what the next lecture in this free embedded systems course covers.
Frequently Asked Questions
Can an overlay create device nodes?
No. Overlays are a plain file copy. Special files and non-default permissions need a device table or a proper Buildroot package.
Does Buildroot rebuild files inside an overlay automatically?
No. The overlay step only copies whatever is currently in the overlay directory – you must update the source file yourself before the next build.
Can I use more than one overlay directory?
Yes. BR2_ROOTFS_OVERLAY accepts a space-separated list, applied in order, with later overlays winning on path conflicts.
Why did my program crash only on the target, not on my host?
This almost always means it was compiled with the host’s native compiler instead of Buildroot’s cross toolchain, producing a binary linked against the wrong C library.
Should application source code always be added as an overlay?
Only for small, already-built artifacts. Anything that needs its own compile step inside the Buildroot build belongs in a proper package instead.
Where do overlay files end up if there is a path collision with a package?
Whichever overlay or package installs last during the build wins, so ordering in BR2_ROOTFS_OVERLAY and package install order both matter.
Continue the Free Embedded Linux Course
Next up: turning your own program into a real Buildroot package with Config.in and a makefile.
Next Lecture Browse the Full Course
2 Comments