Create your own
Lesson illustration

Build, Host, and Target Systems: Choosing an ARM64 ABI, C Library, and Cross-Toolchain

Welcome back. The preceding lesson followed an AM62x-class board from reset release through Boot ROM, SRAM-resident early firmware, and DDR initialization. Once DDR is working, the board can run substantial boot and operating-system software—but that software is normally built somewhere else: your Ubuntu workstation.

This lesson establishes the vocabulary and engineering choices that make that workflow reliable. You will distinguish the formal build, host, and target roles; separate ARM32 terminology from the AM62x gateway’s ARM64 ABI; and make a justified initial choice of C library and cross-toolchain for the automotive/industrial gateway project.


One word, two meanings: “host”

In everyday embedded-Linux work, people commonly say:

  • host machine: the developer’s Ubuntu workstation;
  • target: the AM62x board running the product software.

That vocabulary is practical, and Yocto uses it frequently. But compiler documentation, Autotools, and toolchain construction use build, host, and target with narrower definitions. The confusion comes from using “host” in both systems.

The formal definitions describe a particular program being built:

RoleFormal meaningQuestion to ask
BuildThe system on which the compilation commands execute.“Where does this build process run?”
HostThe system on which the program being built will execute.“Where will this newly built program run?”
TargetThe system for which a compiler being built emits code.“What code will this compiler generate?”

The third term is strictly needed when the program being built is itself a compiler.

Consider your laptop and future AM62x gateway:

  • Your Ubuntu laptop has an Intel CPU.
  • The AM62x application cores are ARM Cortex-A53 processors, capable of running AArch64 Linux.
  • A cross-compiler runs on the laptop but emits AArch64 ELF binaries for the board.

Now distinguish two different builds.

Case 1: Building the cross-compiler

Suppose Yocto builds an AArch64 GCC toolchain.

RoleSystem
BuildUbuntu workstation
HostUbuntu workstation, because the compiler executable runs there
TargetAArch64 Linux, because the compiler emits code for the AM62x

This is the canonical cross-toolchain arrangement: build and host are , while target is AArch64 Linux.

Case 2: Building the gateway application with that compiler

Now use that cross-compiler to build a CAN-to-IP service.

RoleSystem
BuildUbuntu workstation
HostAArch64 Linux on the AM62x
TargetNot applicable in the formal compiler-construction sense

For an Autotools application, this is why a command such as ./configure --host=aarch64-... means “build an application that runs on AArch64.” It does not mean the AM62x is compiling the application. The board is the host of the application.

A build may also generate small helper programs such as protocol generators, configuration utilities, or code generators. Those helpers must execute on the build machine, so they are built with the native compiler. The final gateway service is built with the AArch64 cross-compiler. A competent build system keeps those two categories distinct.

A cross-compiled AArch64 executable generally cannot run directly on your Intel laptop. QEMU or another emulator can make it executable for testing, but emulation does not change the binary’s architecture or turn the laptop into the target.

Anatomy of Cross-Compilation Toolchains

Watch Anatomy of Cross-Compilation Toolchains from The Linux Foundation. It establishes the formal vocabulary, then connects target identity, C libraries, sysroots, and ABI compatibility.

Watch the core model for the build-host-target definitions, the distinction between bare-metal and Linux toolchains, and the toolchain’s core components. Then watch C library choices, focusing on why a libc is both a runtime component and an ABI commitment. Finally, watch ABI and SDK scope for calling conventions, ARM ABI incompatibility, and the difference between a toolchain and an SDK.


Toolchain identity: architecture is necessary, but not sufficient

An AArch64 processor can execute AArch64 instructions, but that alone does not make separately built binaries interoperable. They must agree on an Application Binary Interface (ABI).

An ABI is the binary-level contract that specifies such things as:

  • which registers carry function arguments and return values;
  • stack alignment and structure layout rules;
  • sizes and alignment of fundamental types;
  • ELF object conventions, relocations, and symbol linkage;
  • dynamic-loader and shared-library expectations;
  • the boundary between Linux userspace and kernel system calls.

Source-level compatibility is not enough. If a vendor binary, your application, and its shared libraries use incompatible ABIs, the linker may reject them. In more subtle cases, they could link but behave incorrectly because each side interprets arguments or memory layout differently.

ARM32 and ARM64 are not interchangeable labels

The AM62x Cortex-A53 cluster can support both AArch32 and AArch64 execution states. For the Linux gateway, however, choose one userspace architecture deliberately.

The ARM terminology is easy to blur:

TermApplies toMeaning
ARM EABIPrimarily 32-bit ARM LinuxA family of embedded ABI conventions for AArch32 software.
EABIhf / gnueabihf32-bit ARM LinuxA hard-float variant in which floating-point arguments use floating-point registers.
AArch6464-bit Arm execution stateThe 64-bit instruction-set architecture, often called arm64 by Linux.
AAPCS64AArch64The standard procedure-call convention for 64-bit Arm code.
LP64Typical 64-bit Linux data modellong and pointers are 64 bits; int remains 32 bits.

For an AM62x gateway running a 64-bit Linux distribution, the relevant choice is AArch64 with AAPCS64 and the LP64 data model. Do not describe this choice as “EABI hard-float.” That wording belongs to the older 32-bit ARM ecosystem.

This is more than terminology. You cannot combine an AArch32 arm-linux-gnueabihf library with an AArch64 aarch64-linux-gnu application. They contain different instruction encodings, use different registers and calling conventions, and have different ELF formats.

A useful distinction is:

  • -march or -mcpu controls the instruction set and processor tuning;
  • the ABI controls how separately compiled binary components cooperate.

For example, Cortex-A53 tuning may be appropriate for AM62x application software, but it must not cause you to select instructions unsupported by the actual deployment CPU. In a Yocto product, machine tuning metadata should establish this consistently. Avoid scattering hand-written CPU flags through individual application Makefiles.


Triplets are labels, not complete architecture decisions

Toolchains and Autotools commonly use names such as:

x86_64-pc-linux-gnu
aarch64-unknown-linux-gnu
aarch64-poky-linux

These are usually called triplets, despite often containing four fields. Their intent is to identify a system broadly through processor architecture, vendor label, kernel or operating-system family, and sometimes C-library or ABI information.

Interpret them carefully:

  • x86_64 identifies the 64-bit Intel/AMD architecture of your Ubuntu laptop.
  • aarch64 identifies 64-bit Arm code generation.
  • linux identifies a Linux userspace target, rather than bare metal.
  • gnu commonly signals GNU userspace conventions and glibc.
  • poky is a Yocto/Poky project identifier, not the silicon vendor.
  • the “vendor” field is often only an identifier; it does not necessarily mean TI, Arm, or your board manufacturer.

A triplet is useful evidence, but it is not the entire compatibility specification. It does not independently prove the correct kernel version, CPU tuning, glibc configuration, distribution features, package revisions, or target sysroot contents. In a Yocto product, the active machine, distribution, layers, and generated SDK together define the real platform contract.

Toolchain Technical Notes

Read this Linux From Scratch technical note for a careful formal treatment of the terms that build systems use. Its “Canadian Cross” example is more advanced than the gateway workflow, but it makes the role names precise.

In the opening definitions, read the build-host-target model, including the table that follows it. Then, in “Implementation of Cross-Compilation for LFS,” read the explanation beginning the triplet discussion. In the later cross-compilation guidance, focus on why an explicitly specified --host selects the cross tools and why target binaries must not be run accidentally during configuration.


What a Linux cross-toolchain contains

It helps to separate the visible compiler command from the complete development environment.

When you invoke an AArch64 GCC driver, it coordinates several components:

  1. GCC front ends and runtime libraries compile C or C++ and provide support such as libgcc and, where relevant, libstdc++.
  2. Target binutils include the assembler and linker that understand AArch64 ELF objects, plus inspection tools such as readelf, objdump, and nm.
  3. Linux UAPI kernel headers describe the stable kernel-to-userspace interfaces: system calls, ioctl structures, networking definitions, and similar interfaces.
  4. A target C library provides standard C and POSIX functions, interfaces with Linux system calls, and provides the dynamic loader for dynamically linked programs.
  5. A target sysroot supplies the coherent target headers, libraries, linker metadata, and often debug symbols used during compilation and linking.

The sysroot is a logical copy of the target’s development-facing filesystem. It is not simply a directory of random headers. If your application includes stdio.h, or links against libssl, the compiler must find the AArch64 versions built for your selected distribution—not the incompatible versions installed under your Ubuntu host’s /usr/include and /usr/lib.

A target root filesystem contains runtime files deployed to the board. A sysroot contains the development view needed to build compatible software. There is overlap, particularly for libraries, but they have different purposes and should be produced from the same platform configuration.

This diagram depicts Yocto’s cross-development model: a build host produces a target image and a relocatable SDK, an SDK machine runs the installed cross tools to build target applications, and the target device runs the image and those applications. The diagram’s older internal tool names are less important than the separation of execution environments.

The supplied diagram uses SDKMACHINE for the system that installs and runs the SDK. It may be the same physical Ubuntu laptop as the build host, but it need not be. A CI server might build the image and SDK, while an application developer installs the SDK on a separate workstation.

Some internal names in the diagram, such as gcc-crosssdk and gcc-cross-canadian, are useful historical Yocto terminology. Do not make them your main mental model. For application development, the important facts are:

  • the cross-compiler executable runs on the SDK machine;
  • it uses a target sysroot generated to match the product;
  • the output application runs on the AM62x target.

1 Introduction — The Yocto Project ® 6.0-tip documentation

Read the Yocto Project SDK manual introduction to connect the abstract cross-toolchain model to the SDK you will later generate for the gateway.

In Section 1.1, “eSDK Introduction,” read the SDK components. Notice that the setup script, toolchain, and image-matched development files form one environment. Then read Section 1.1.1, “The Cross-Development Toolchain,” followed by Section 1.1.2, “Sysroots”; focus especially on the sysroot definition.


Choosing a C library for the gateway

A Linux C library, or libc, is not merely a collection of familiar functions such as printf() and memcpy(). It provides the interface through which programs use the Linux kernel for files, processes, threads, sockets, timers, and many other services. It also determines important runtime facts, including the dynamic loader used by dynamically linked executables.

The three choices most relevant to embedded Linux are:

LibraryMain strengthsMain costs and risksTypical fit
glibcBroad POSIX and GNU compatibility; mature locale, threading, networking, NSS, and dynamic-linking ecosystem; strong third-party and vendor support.Larger footprint than minimalist alternatives; static linking is not its primary deployment model.Full-featured Linux products with long support lifecycles.
muslSmall, clean implementation; strong static-linking workflow; permissive MIT license; useful for compact systems.Some software assumes glibc-specific APIs or behavior; vendor binary and SDK compatibility must be checked explicitly.Small appliances, static recovery utilities, deliberately musl-based distributions.
uClibc-ngConfigurable and historically useful under extreme flash constraints.Smaller mainstream ecosystem for new gateway development; compatibility and maintenance effort require stronger justification today.Legacy or unusually constrained Linux systems.

The choice creates a boundary: binaries and shared libraries built against glibc are not binary-compatible with musl equivalents. You do not “switch libc” by copying one file into a running image. You rebuild the relevant userspace, SDK, and application stack for the selected libc.

Static linking can appear to bypass this issue because it embeds much of the library code in an executable. It can be valuable for a deliberately self-contained recovery or diagnostic program. But it also increases artifact size and means that library security fixes require rebuilding and redeploying every affected executable. It is not a substitute for selecting and maintaining a coherent product libc.


A defensible initial decision for the AM62x gateway

Use this stated product constraint for the decision:

Product constraint: A long-lived automotive or industrial edge gateway will run on an AM62x board with CAN-FD and Ethernet connectivity, persistent storage, remote management, an OTA update mechanism, systemd-supervised services, and a vendor-supported Yocto BSP. The product must remain maintainable through a multi-year security and compliance lifecycle, and it may integrate vendor-provided userspace components.

Under that constraint, the recommended initial platform choice is:

Decision areaInitial choiceJustification
Userspace architecture and ABIAArch64 Linux, AAPCS64, LP64AM62x Cortex-A53 cores support 64-bit execution. AArch64 is the natural ABI for a modern ARM64 Linux distribution and avoids maintaining an unnecessary 32-bit compatibility boundary.
CPU baselineARMv8-A, tuned consistently for Cortex-A53 where the BSP permitsThe target is specifically Cortex-A53. The machine configuration should own CPU features and tuning so every package receives compatible settings.
C libraryglibcThe gateway favors broad middleware compatibility, standard Linux services, vendor BSP alignment, dynamic updates, debugging tools, and a long support lifecycle over saving the last fraction of rootfs space.
Product build environmentRelease-pinned Yocto/Poky plus the AM62x vendor BSP layersImage, kernel, boot artifacts, libraries, package metadata, and SDK are built from one controlled configuration. This supports traceability and reproducibility.
Application-development toolchainYocto-generated AArch64 SDK or eSDK matching that imageThe SDK’s compiler and sysroot match the selected machine, distribution, libc, and installed development packages. It avoids accidental linkage to Ubuntu libraries.
Host execution environmentUbuntu Your current dual-boot Ubuntu workstation runs BitBake, native build tools, and the AArch64 cross-toolchain. Its capacity will need deliberate tuning later, but its architecture is suitable for the workflow.

This is a justified baseline, not an irreversible decision. A strict flash-size ceiling, a requirement for a statically linked rescue environment, or a vendor middleware package available only for a different libc could change it. Such a change must be evaluated as a platform-wide decision, not an individual application flag.

For this gateway, do not use a generic Ubuntu cross-compiler as the final product toolchain merely because it can emit AArch64 instructions. It can be useful for short experiments. But product applications should be built with the SDK generated from the same Yocto release and configuration that produced the deployed image.

A concise architecture-decision record for the private repository can capture the choice:

Decision: AArch64/AAPCS64 Linux userspace using glibc.
Toolchain: Yocto-generated SDK matched to the AM62x machine and gateway distribution.
Reason: Vendor-BSP compatibility, full Linux service ecosystem, maintainable multi-year product lifecycle.
Rejected baseline: 32-bit ARM hard-float, musl, and generic distro cross-toolchains.
Revisit when: A quantified image-size limit, vendor binary compatibility result, or safety/security requirement changes.

The sanitized decision and rationale are suitable for the future public portfolio; keep exact vendor SDK versions, proprietary middleware details, and security-sensitive build configuration private.


Key takeaways

  • Build is where a build runs; host is where the program being built runs; target is the architecture produced by a compiler being built.
  • In normal application cross-compilation, the AM62x is formally the application’s host, even though engineers commonly call it the target board.
  • An AM62x Linux gateway should use AArch64, the AAPCS64 procedure-call ABI, and the normal 64-bit LP64 data model—not ARM32 EABIhf terminology.
  • A cross-toolchain includes more than GCC: binutils, kernel UAPI headers, libc, runtime libraries, and a coherent target sysroot are all part of the platform contract.
  • For the stated long-lived connected-gateway constraint, glibc with a Yocto-generated, image-matched AArch64 SDK is the strongest initial choice.
  • The architecture, libc, machine tuning, sysroot, and SDK must remain aligned; individual binaries cannot safely mix these platform decisions.

Next, you will configure a cross-toolchain sysroot and use it to cross-compile an ARM64 C program while proving that no host headers or host libraries have leaked into the build.

Can't find a good explanation? Sign up and we'll make it for you

Sign up