What is Cross Compiling With Makefiles in Linux-Free Embedded Linux Training

Cross Compiling With Makefiles
How CROSS_COMPILE and ARCH drive a Makefile-based build for your target

Once you have a working cross toolchain, the next step in any free embedded Linux course is
actually using it to build real software for your target board. Most beginners assume cross compiling is some
exotic process, but for projects built with plain Makefiles, it usually comes down to overriding two variables:
CROSS_COMPILE and, for certain projects, ARCH. This lecture is part of our
free embedded systems course and walks through exactly how that works, with an original demo
project you can build yourself.

CROSS_COMPILE
Makefile cross compiling
embedded Linux toolchain
ARCH variable
free linux kernel development course

What You Will Learn

  • Why plain-Makefile projects are the easiest cross compile targets
  • The role of CROSS_COMPILE and how GNU Make implicit rules use it
  • When and why ARCH is also required
  • Building an original demo utility for an ARM target
  • Verifying the output binary actually targets the right architecture
  • Common mistakes that silently produce a host binary instead of a target one

Prerequisites

Before You Start

  • A working cross toolchain on your PATH (covered in earlier lectures of this course)
  • Basic familiarity with GNU Make and Makefiles
  • A Linux host machine (native or VM)

Why Plain Makefile Projects Are Simple

GNU Make ships with a set of implicit rules for compiling C and C++ code. These implicit rules do not hardcode
the compiler name as gcc — instead they reference variables like CC, which itself
defaults to a combination involving CROSS_COMPILE in projects that follow the Linux kernel’s build
convention. A huge number of embedded projects — the Linux kernel itself, U-Boot, BusyBox, and countless smaller
utilities — deliberately follow this convention so that a single variable switch is all that is needed to retarget
the entire build.

In a Makefile written this way, you will typically find lines resembling:

Typical CROSS_COMPILE Usage In A Makefile
CROSS_COMPILE ?=
CC := $(CROSS_COMPILE)gcc
LD := $(CROSS_COMPILE)ld
AR := $(CROSS_COMPILE)ar
STRIP := $(CROSS_COMPILE)strip

Because CROSS_COMPILE defaults to an empty string, the exact same Makefile builds natively on your
host when you don’t set it, and builds for your target the moment you do. This is the entire trick — no separate
cross-compile Makefile is needed.

Setting CROSS_COMPILE

You can pass CROSS_COMPILE as a one-off argument on the make command line, or export it as a shell
variable so every subsequent make invocation in that shell session picks it up automatically.

Two Equivalent Ways To Set CROSS_COMPILE

$ make CROSS_COMPILE=arm-linux-gnueabihf-

# or, for the rest of the shell session:
$ export CROSS_COMPILE=arm-linux-gnueabihf-
$ make

Note the trailing dash on the prefix. GNU Make simply concatenates CROSS_COMPILE with tool names
like gcc, so the dash is what separates the toolchain triplet from the tool name, producing
arm-linux-gnueabihf-gcc.

When You Also Need ARCH

A few large, well known projects — most notably the Linux kernel and U-Boot — go a step further than a normal
Makefile project. Their build systems select architecture-specific source directories, headers, and assembly
routines based on a second variable, ARCH. For these projects, setting CROSS_COMPILE
alone only changes which compiler is invoked; you also need ARCH to tell the build which CPU family’s
code paths to compile.

Variable Purpose Required For
CROSS_COMPILE Prefixes every toolchain tool name (gcc, ld, ar, strip) Almost all Makefile-based embedded projects
ARCH Selects architecture-specific source trees / headers Linux kernel, U-Boot, and similarly structured projects
Kernel-Style Build Needing Both Variables
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j$(nproc)

A simple utility Makefile that just compiles a handful of .c files, by contrast, has no notion of
ARCH at all — the compiler prefix alone is enough, because the compiler itself already knows which
instruction set and ABI it targets.

Hands-On: Building An Original Demo Utility

Let’s build a small original tool, ep_sysinfo, that prints basic information about the machine it
runs on. We’ll compile it natively first, then cross compile it for an ARM target using the same Makefile,
changing nothing but CROSS_COMPILE.

ep_sysinfo.c
#include <stdio.h>
#include <sys/utsname.h>int main(void)
{
struct utsname info;if (uname(&info) != 0) {
perror(“uname”);
return 1;
}printf(“ep_sysinfo report\n”);
printf(” system: %s\n”, info.sysname);
printf(” node: %s\n”, info.nodename);
printf(” release: %s\n”, info.release);
printf(” machine: %s\n”, info.machine);return 0;
}
Makefile
CROSS_COMPILE ?=
CC := $(CROSS_COMPILE)gcc
CFLAGS := -Wall -O2TARGET := ep_sysinfoall: $(TARGET)$(TARGET): ep_sysinfo.c
$(CC) $(CFLAGS) -o $@ $<clean:
rm -f $(TARGET)

Build it natively first, just to confirm it works:

Native Build And Run
$ make
gcc -Wall -O2 -o ep_sysinfo ep_sysinfo.c$ ./ep_sysinfo
ep_sysinfo report
system: Linux
node: devbox
release: 6.8.0-generic
machine: x86_64

Now clean and rebuild with CROSS_COMPILE pointed at an ARM toolchain (substitute whatever prefix
your own toolchain uses, e.g. arm-linux-gnueabihf- or aarch64-linux-gnu-):

Cross Build — Same Makefile, One Variable Changed
$ make clean
rm -f ep_sysinfo$ make CROSS_COMPILE=arm-linux-gnueabihf-
arm-linux-gnueabihf-gcc -Wall -O2 -o ep_sysinfo ep_sysinfo.c

Verifying You Actually Cross Compiled

It’s easy to think a build succeeded when in fact CROSS_COMPILE was silently ignored — for example
if it was misspelled, or the toolchain prefix doesn’t exist on your PATH, causing Make to fall back to the host
compiler in some misconfigured setups. Always confirm the resulting binary’s architecture with file:

Confirming The Target Architecture
$ file ep_sysinfo
ep_sysinfo: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV),
dynamically linked, interpreter /lib/ld-linux-armhf.so.3, …

If file reports x86-64 instead of ARM, the cross compiler was never
actually invoked — go back and check that the toolchain prefix is spelled exactly right and is present on your
PATH.

Common Mistakes And Troubleshooting

Frequent Pitfalls

  • Forgetting the trailing dash on CROSS_COMPILE, producing an invalid tool name
  • Not running “make clean” before switching targets, leaving stale host-built objects
  • Assuming every project needs ARCH — most plain Makefile projects don’t
  • Toolchain prefix not on PATH, causing silent fallback or a “command not found” error
  • Mixing object files built with different CROSS_COMPILE settings in one binary

Best Practices

  • Always export CROSS_COMPILE for a whole build session rather than retyping it per command
  • Run “file” on every output binary before deploying it to hardware
  • Keep separate build directories for native and cross builds to avoid stale-object bugs
  • Pin the exact toolchain version in your project notes — ABI mismatches are hard to debug later

Summary And Key Takeaways

  • CROSS_COMPILE prefixes toolchain tool names and is enough for most simple Makefile projects
  • ARCH is only needed for build systems, like the kernel and U-Boot, that branch on CPU family
  • The same Makefile builds natively or for a target purely based on this one variable
  • Always verify the resulting binary’s architecture with the file command

This hands-on foundation is essential groundwork before moving to more complex build systems — which is exactly
where our next lecture in this free linux development course picks up, covering Autotools-based
projects.

FAQ

What does the trailing dash in CROSS_COMPILE mean?

It’s the separator between the toolchain triplet and the tool name — Make concatenates CROSS_COMPILE directly
with strings like “gcc”, so without the dash you’d get an invalid tool name such as “arm-linux-gnueabihfgcc”.

Do I need ARCH for every embedded project?

No. Only projects whose build system branches on CPU family — like the Linux kernel and U-Boot — need ARCH.
Ordinary utility Makefiles rely on the compiler prefix alone.

How do I know my cross compile actually worked?

Run the “file” command on the output binary and confirm the reported architecture matches your target, not
your host machine.

Can I set CROSS_COMPILE permanently instead of typing it every time?

Yes — export it as a shell environment variable, or hardcode a default inside your Makefile if the project is
always built for one fixed target.

What happens if I forget to run make clean between a native and cross build?

Make may reuse stale object files built for the wrong architecture, which typically causes confusing linker
errors or, worse, a binary that mixes incompatible object formats.

Is CROSS_COMPILE specific to GNU Make?

The variable itself is just a convention popularized by the Linux kernel’s build system, but any Makefile can
adopt the same pattern — it isn’t a built-in Make feature.

 

Continue Your Embedded Linux Journey

Next up: cross compiling Autotools-based projects like SQLite.

Leave a Reply

Your email address will not be published. Required fields are marked *