Buildroot Cross-Compilation Failures: Tracing Host Libraries, pkg-config and Sysroot Leaks
A cross-build can fail because it uses the wrong library, the wrong compiler, or the right compiler with the wrong search path. It can also fail because a build step tries to execute a program compiled for the target. Diagnose the role of each artifact before editing environment variables. This guide uses Buildroot 2025.02.18 and its CMake 3.31.12 package as fixed references; the examples illustrate investigation and are not a completed cross-build.

Identify where each program is supposed to run
Make a host/target ledger for the failing package. A schema compiler invoked while building must run on the build host. A shared library linked into the firmware application must match the target. The cross-compiler itself runs on the host while producing target code. This distinction is about execution and output roles, not about whether the file happens to live in a directory named host.
Read the first failed command and classify it. An execution-format error during code generation suggests the wrong executable was launched. A linker reporting an incompatible object suggests an architecture mismatch. A missing symbol may instead be an API, ABI or link-order problem. Treat these as starting hypotheses. Capture the file involved and inspect it before deleting caches or installing more native development packages.
Follow the actual compiler and linker command
Enable verbose output and retain the complete compile and link lines. Check the compiler executable, its target identity, sysroot, include paths, library paths and explicit filenames. Trace suspect arguments back to the recipe, upstream Makefile, CMake cache or environment. A single absolute library path can bypass otherwise reasonable discovery rules and pull in a developer workstation’s library.
Do not reject every path containing /usr. The compiler can legitimately use host-side internal resources, and a target sysroot commonly contains its own usr directory. Resolve the full path and determine what kind of file is being accessed. Record PATH, CC, CXX, CFLAGS, LDFLAGS and relevant SDK variables with secrets removed. Compare only the variables relevant to the build; dumping a whole CI environment can expose credentials while obscuring the actual difference.
Inspect the paths returned by pkg-config
Determine which pkg-config-compatible executable the configure step actually invokes, which .pc files it reads and what flags it returns. Record the working environment of that step, not an interactive shell started later with different variables. Inspect prefixes and include/library values in the chosen metadata, then check whether those locations belong to the selected target sysroot.
PKG_CONFIG_PATH, PKG_CONFIG_LIBDIR and PKG_CONFIG_SYSROOT_DIR affect different parts of discovery and path handling; their behavior also depends on the implementation. Prefer the environment supplied by the selected Buildroot package infrastructure, rather than exporting a workstation-wide override. In this baseline, inspect Buildroot’s pkgconf integration and the actual wrapper involved. A correct-looking result from a different executable does not explain the failing configuration step.
| Observed symptom | First evidence to inspect | Avoid this shortcut |
|---|---|---|
| Generator fails with execution-format error | Generator ELF and command invocation | Running the target tool through a guessed workaround |
| Linker rejects a library as incompatible | Resolved library path and ELF machine | Adding more global -L directories |
| Build works only on one developer host | Compiler/discovery logs and relevant environment | Installing native libraries until it passes |
| Correct architecture; undefined symbols remain | API/ABI version, link order, enabled features | Assuming every link failure is host contamination |
| Binary starts on build host unexpectedly | Actual compiler and output ELF | Treating native execution as target validation |
Review the build system cross-compilation integration
For CMake, inspect the supplied toolchain file and effective cache, including compiler selection and find-root behavior. The CMake 3.31 toolchain documentation explains how program lookup and target library lookup can have different roots. Use Buildroot’s cmake-package infrastructure instead of launching an unrelated native CMake configuration from a custom hook.
Here is a minimal upstream correction for an illustrative target named telemetry. Remove a hard-coded host library and use a discovered imported target:
# Remove this workstation-specific linkage:
# target_link_libraries(telemetry PRIVATE /usr/lib/x86_64-linux-gnu/libz.so)
find_package(ZLIB REQUIRED)
target_link_libraries(telemetry PRIVATE ZLIB::ZLIB)
The FindZLIB module defines that imported target. This change still requires the proper Buildroot zlib dependency and a correct toolchain search environment. It cannot repair an incorrectly selected sysroot by itself. If configuration tries to run target code, replace or properly parameterize that probe; never invent a successful probe result without evidence.
Separate build-time generators from target artifacts
Identify every executable launched during generation, including tools invoked indirectly by scripts. Give host tools explicit package dependencies and pass their controlled paths through the build system’s supported options. If a project builds both a generator and the final library, separate their builds or use an existing host variant where appropriate. Do not run a target generator on the host merely because it was built a few lines earlier.
Record the generator version together with its input schema and generated output. Repeated builds should not depend on a pre-generated file left by a different version. Check whether generated content embeds host paths or timestamps, and keep those observations separate from claims of byte reproducibility. The diagnostic goal here is to make the build tool selection explicit; the project can define stricter output reproducibility requirements as a separate test.
Check artifact architecture and dynamic dependencies
Inspect the produced file without executing it. Use file and a suitable readelf implementation to examine the ELF machine, class, interpreter and dynamic NEEDED entries. The following shell fragment assumes READELF names the chosen tool and APP names a built artifact; it is an inspection template, not recorded output.
file -- "$APP"
"$READELF" -h "$APP"
"$READELF" -l "$APP"
"$READELF" -d "$APP"
Compare the requested interpreter and runtime libraries with the intended root filesystem. A statically linked program may have no interpreter or dynamic dependency section, which changes the interpretation. Avoid using host ldd as a generic cross-architecture inspection method, and do not directly run an unknown target binary. The GNU readelf reference documents the inspected sections. Static evidence still needs an appropriate target smoke test before claiming runtime compatibility.
Retest in isolation and state the remaining scope
Rebuild with fixed sources, toolchain and relevant host dependencies in a fresh output directory. Remove unrelated personal SDK settings from the declared test environment, then compare the repaired command and discovery results with the failing ones. When a second supported host environment is available, use it to test the hypothesis that a native installation supplied an accidental dependency. Record environment differences rather than attributing every change to “isolation.”
The repair report should identify the contaminating path or wrong-role executable, its producer, the changed integration point and the artifact evidence. If the remaining issue occurs only when loading libraries on the device, state that runtime scope explicitly. Return to dependency review for missing producers and use package CI to retain the relevant regression. A green build in one cleaned shell is a useful observation, not universal host independence.
References and version boundary
Examples are engineering illustrations for the stated version. No board execution, full cross-build, performance benchmark or field-deployment result is claimed.
- Buildroot 2025.02.18 source archive
- Buildroot manual package infrastructure
- Buildroot 2025.02.18 pkgconf integration
- CMake 3.31 toolchains
- CMake 3.31 FindZLIB
- GNU readelf reference
For a focused BSP maintenance or build review, contact Obeita with the relevant version baseline, a sanitized configuration and the first complete failure log. Include the hardware or toolchain scope and the result you need to establish.