How-To-Tutorials · September 5, 2025

How to Cross-Compile C Programs for Raspberry Pi Using GCC on Ubuntu

how-to-cross-compile-c-programs-for-raspberry-pi-using-gcc-on-ubuntu.png

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

  1. 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 aarch64 toolchain, not the older 32-bit arm-linux-gnueabihf one:

    sudo apt update
    sudo apt install gcc-aarch64-linux-gnu make

    If 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-gnueabihf

    Verify the install worked:

    aarch64-linux-gnu-gcc --version

    You should see the GCC version info. On Ubuntu 24.04, this ships GCC 13.x which is perfectly fine for Pi targets.

  2. Create your C program

    Set up a project directory and write a quick test program:

    mkdir ~/pi-crosscompile && cd ~/pi-crosscompile

    Create 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.

  3. 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) *.o
    

    Note the -O2 flag — 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.

  4. Build it

    Run make in your project directory:

    make

    You should see the compile and link steps. Verify the output binary is actually an ARM executable:

    file hello

    This should report something like ELF 64-bit LSB executable, ARM aarch64. If it says x86-64, you're using the wrong compiler.

  5. 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
    ./hello

    You 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 scp or rsync target 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, not arm-linux-gnueabi-gcc (that's the older 32-bit variant).
  • Binary won't run on the Pi ("cannot execute binary file"):
    • Architecture mismatch. Run uname -m on the Pi. If it says aarch64, you need the 64-bit toolchain. If it says armv7l, you need the 32-bit one.
    • Make sure the binary has execute permissions: chmod +x hello.
  • 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 /usr and /lib directories) 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.

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.