How to Cross-Compile C Programs for Raspberry Pi Using GCC on Ubuntu
Why Cross-Compile?
Compiling C code directly on a Raspberry Pi works fine for small projects, but it's painfully slow for anything substantial. A Pi 5 is much faster than older models, but it still can't match a modern x86 workstation for compile times. Cross-compiling lets you build ARM binaries on your fast Ubuntu machine and just ship the result to the Pi.
This is especially useful if you're doing iterative development, building large projects, or working with headless Pi setups where you'd rather not install a full development toolchain on the target.
Prerequisites
- Ubuntu 22.04 or later (works on 24.04 LTS too)
- Basic C programming knowledge
- Comfort with the Linux command line
- A Raspberry Pi for testing (Pi 4 or Pi 5 — both use 64-bit ARM)
Parts/Tools
- GCC cross-compiler toolchain for ARM
- Make build utility
- Text editor of your choice (VS Code, Vim, Nano, etc.)
- SSH access to your Raspberry Pi
Steps
-
Install the cross-compiler toolchain
First, grab the ARM cross-compiler. For modern Raspberry Pi OS (which is 64-bit on Pi 4 and Pi 5), you want the
aarch64toolchain, not the older 32-bitarm-linux-gnueabihfone:sudo apt update sudo apt install gcc-aarch64-linux-gnu makeIf you're targeting an older 32-bit Raspberry Pi OS installation (or a Pi Zero/Zero 2W in 32-bit mode), use the 32-bit toolchain instead:
sudo apt install gcc-arm-linux-gnueabihfVerify the install worked:
aarch64-linux-gnu-gcc --versionYou should see the GCC version info. On Ubuntu 24.04, this ships GCC 13.x which is perfectly fine for Pi targets.
-
Create your C program
Set up a project directory and write a quick test program:
mkdir ~/pi-crosscompile && cd ~/pi-crosscompileCreate
hello.c:#include <stdio.h> int main(void) { printf("Hello from cross-compiled ARM binary!\n"); return 0; }Nothing fancy — just enough to confirm the toolchain works end to end.
-
Write a Makefile
A Makefile keeps things repeatable and lets you switch between native and cross builds easily. Create a file called
Makefile:CC = aarch64-linux-gnu-gcc CFLAGS = -Wall -Wextra -O2 TARGET = hello all: $(TARGET) $(TARGET): hello.o $(CC) $(CFLAGS) -o $@ $^ hello.o: hello.c $(CC) $(CFLAGS) -c $< clean: rm -f $(TARGET) *.oNote the
-O2flag — you almost always want optimization enabled for ARM targets. The Pi doesn't have the raw clock speed to brute-force unoptimized code the way a desktop CPU can.Watch out: Makefile rules require actual tab characters for indentation, not spaces. If you get "missing separator" errors, that's the problem.
-
Build it
Run
makein your project directory:makeYou should see the compile and link steps. Verify the output binary is actually an ARM executable:
file helloThis should report something like
ELF 64-bit LSB executable, ARM aarch64. If it says x86-64, you're using the wrong compiler. -
Transfer and run on the Raspberry Pi
Copy the binary to your Pi over SSH:
scp hello pi@your-pi-hostname:~Then SSH in and run it:
ssh pi@your-pi-hostname chmod +x hello ./helloYou should see:
Hello from cross-compiled ARM binary!Pro tip: if you're doing this a lot, set up an SSH key so you don't have to type your password every time. You can also add an
scporrsynctarget to your Makefile to automate the deploy step.
Troubleshooting
- "command not found" when running the cross-compiler:
- Make sure the package installed correctly:
dpkg -l | grep aarch64. If it's not listed, re-run the apt install command. - Check the exact binary name. It's
aarch64-linux-gnu-gcc, notarm-linux-gnueabi-gcc(that's the older 32-bit variant).
- Make sure the package installed correctly:
- Binary won't run on the Pi ("cannot execute binary file"):
- Architecture mismatch. Run
uname -mon the Pi. If it saysaarch64, you need the 64-bit toolchain. If it saysarmv7l, you need the 32-bit one. - Make sure the binary has execute permissions:
chmod +x hello.
- Architecture mismatch. Run
- Linker errors about missing libraries:
- If your program uses libraries beyond libc, you'll need the ARM versions of those libraries. The simplest approach is to copy the Pi's sysroot (the
/usrand/libdirectories) to your build machine and point the cross-compiler at them with--sysroot. - For more complex projects, consider using a Docker-based build environment or a tool like crosstool-NG for a complete cross-compilation sysroot.
- If your program uses libraries beyond libc, you'll need the ARM versions of those libraries. The simplest approach is to copy the Pi's sysroot (the
Where to Go from Here
Once you've got basic cross-compilation working, you can scale this up. CMake has solid cross-compilation support via toolchain files. PlatformIO v6.x can also manage cross-builds for embedded Linux targets. And if you're building kernel modules or a full OS image, look into Buildroot or the Yocto Project — they're purpose-built for cross-compiled Linux system images.