Arm GNU Toolchain
How to compile/generate AArch32 code and run on an Linux/AArch64?
How to build AArch64-ELF for a popular embedded target board under Windows/x86_64 or macOS/arm64?
Related issues and frequently asked questions
arm64 - Can I run an ARM32 bit App on an ARM64bit platform which is running Ubuntu 16.04 - Ask Ubuntu
arm - how to use aarch64-linux-gnu-objdump to disassemble V7 mode instructions (A32,T32) - Stack Overflow
raspberrypi - Compiling ARMv7A (AArch32) code on an ARMv8A (AArch64) machine? - Ask Ubuntu
gcc - Compile and run a 32bit binary on Armv8 (aarch64) running 64bit linux - Stack Overflow
Before starting, see previous post Compiler Toolchain to take a glimpse at concepts of Cross Compiler Toolchain.
Linaro empowers rapid product deployment within the dynamic Arm ecosystem.
Arm GNU Toolchain is a community supported pre-built GNU compiler toolchain for Arm based CPUs.
Arm GNU Toolchain#
Arm GNU Toolchain releases consists of cross toolchains for the following host operating systems:
-
GNU/Linux
- Available for x86_64 and AArch64 host architectures
- Available for bare-metal and Linux targets
-
Windows
- Available for x86 host architecture only (compatible with x86_64)
- Available for bare-metal and Linux targets
-
macOS
- Available for x86_64 and Apple silicon (beta) host architectures
- Available for bare-metal targets only
AArch64 Linux hosted cross toolchains
- AArch32 bare-metal target (arm-none-eabi)
- AArch32 GNU/Linux target with hard float (arm-none-linux-gnueabihf)
- AArch64 ELF bare-metal target (aarch64-none-elf)
macOS (x86_64/Apple silicon) hosted cross toolchains
- AArch32 bare-metal target (arm-none-eabi)
- AArch64 bare-metal target (aarch64-none-elf)
On macOS, type brew search gcc
to search for the gcc compiler toolchain via homebrew.
$ brew search gcc
==> Formulae
aarch64-elf-gcc gcc@11 gcc@6 i686-elf-gcc ghc ncc
arm-none-eabi-gcc gcc@12 gcc@7 libgccjit grc
gcc ✔ gcc@13 gcc@8 riscv64-elf-gcc scc
gcc@10 gcc@5 gcc@9 x86_64-elf-gcc tcc
==> Casks
gcc-aarch64-embedded gcc-arm-embedded gcs icc
The listed Casks gcc-aarch64-embedded
and gcc-arm-embedded
are issued by ArmGNUToolchain, named as "GCC ARM Embedded".
$ brew info gcc-arm-embedded
==> Name
GCC ARM Embedded
==> Description
Pre-built GNU bare-metal toolchain for 32-bit Arm processors
==> Artifacts
arm-gnu-toolchain-13.2.rel1-darwin-x86_64-arm-none-eabi.pkg (Pkg)
/Applications/ArmGNUToolchain/13.2.Rel1/arm-none-eabi/bin/arm-none-eabi-as (Binary)
$ brew info gcc-aarch64-embedded
==> Name
GCC ARM Embedded
==> Description
Pre-built GNU bare-metal toolchain for 64-bit Arm processors
==> Artifacts
arm-gnu-toolchain-13.2.rel1-darwin-x86_64-aarch64-none-elf.pkg (Pkg)
/Applications/ArmGNUToolchain/13.2.Rel1/aarch64-none-elf/bin/aarch64-none-elf-as (Binary)
Cross-compiler#
Cross-compiler | Arm Learning Paths
GCC is available on all Linux distributions and can be installed using the package manager.
This covers gcc
and g++
for compiling C and C++ as a cross-compiler targeting the Arm architecture.
notice#
GCC is often used to cross-compile software for Arm microcontrollers and embedded devices which have firmware and other low-level software. The executables are arm-none-eabi-gcc
and arm-none-eabi-g++
.
GCC is also used to cross compile Linux applications. Applications can be compiled for 32-bit or 64-bit Linux systems.
- The executables for 32-bit are
arm-linux-gnueabihf-gcc
andarm-linux-gnueabihf-g++
. - The executables for 64-bit are
aarch64-linux-gnu-gcc
andaarch64-linux-gnu-g++
.
Software can be compiled on an x86 or Arm host machine.
installation#
On macOS, type brew search gcc
to search for the gcc compiler toolchain via homebrew.
$ brew search gcc
==> Formulae
aarch64-elf-gcc gcc@11 gcc@6 i686-elf-gcc ghc ncc
arm-none-eabi-gcc gcc@12 gcc@7 libgccjit grc
gcc ✔ gcc@13 gcc@8 riscv64-elf-gcc scc
gcc@10 gcc@5 gcc@9 x86_64-elf-gcc tcc
==> Casks
gcc-aarch64-embedded gcc-arm-embedded gcs icc
The following three formulae are issued by gnu.org official, named as "GNU compiler collection".
We can type brew info formula
to show summary of information about the formula.
gcc
: for x86-64/mach-ox86_64-elf-gcc
: for x86_64-elfaarch64-elf-gcc
: for aarch64-elf, with incidental binutilsarm-none-eabi-gcc
: for arm-none-eabi(aarch32-elf?)
Both gcc-aarch64-embedded
and gcc-arm-embedded
are published by developer.arm.com, named as GCC ARM Embedded, described as Pre-built GNU bare-metal toolchain for 32/64-bit Arm processors.
GCC for macOS/arm64
According to GCC Bugzilla – Bug 96168, GCC support for macOS/arm64 is still on the way, check progress at experimental gcc-darwin-arm64.
Let's focus on installation on Debian-based distributions such as Ubuntu, as I will demonstrate the following on my handy rpi3b-ubuntu.
Use the apt
command to install software packages on any Debian based Linux distribution.
$ sudo apt update
$ sudo apt install gcc-arm-none-eabi -y
$ sudo apt install gcc-arm-linux-gnueabihf -y
$ sudo apt install gcc-aarch64-linux-gnu -y
You can check and confirm the package description/information before installation.
$ uname -a
Linux rpi3b-ubuntu 5.15.0-1055-raspi #58-Ubuntu SMP PREEMPT Sat May 4 03:52:40 UTC 2024 aarch64 aarch64 aarch64 GNU/Linux
$ apt search -n gcc-arm-none-eabi
$ apt list gcc-arm-none-eabi
$ apt-cache show gcc-arm-none-eabi
$ apt-cache showpkg gcc-arm-none-eabi
$ apt search -n gcc-aarch64-linux-gnu
Sorting... Done
Full Text Search... Done
$ apt search -n gcc
Sorting... Done
Full Text Search... Done
cross-gcc-dev/jammy 246 all
Tools for building cross-compilers and cross-compiler packages
gcc/jammy,now 4:11.2.0-1ubuntu1 arm64 [installed]
GNU C compiler
gcc-10/jammy-updates,jammy-security 10.5.0-1ubuntu1~22.04 arm64
GNU C compiler
gcc-10-arm-linux-gnueabi/jammy-updates,jammy-security 10.5.0-1ubuntu1~22.04cross1 arm64
GNU C compiler (cross compiler for armel architecture)
gcc-10-arm-linux-gnueabi-base/jammy-updates,jammy-security 10.5.0-1ubuntu1~22.04cross1 arm64
GCC, the GNU Compiler Collection (base package)
gcc-10-arm-linux-gnueabihf/jammy-updates,jammy-security 10.5.0-1ubuntu1~22.04cross1 arm64
GNU C compiler (cross compiler for armhf architecture)
gcc-10-arm-linux-gnueabihf-base/jammy-updates,jammy-security 10.5.0-1ubuntu1~22.04cross1 arm64
GCC, the GNU Compiler Collection (base package)
...
binutils#
We can use ls
, grep
and find
commands to filter arm-linux-gnueabihf-*
in dir /usr/bin to check the packed binutils.
- filter all executables:
- filter symbolic links:
Get started#
To confirm the installation is successful, enter:
$ arm-none-eabi-gcc --version
# newly installed
pifan@rpi3b-ubuntu $ arm-linux-gnueabihf-gcc --version
arm-linux-gnueabihf-gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# previously installed gcc
pifan@rpi3b-ubuntu $ aarch64-linux-gnu-gcc --version
aarch64-linux-gnu-gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
As the well-known shims or wrappers like cpp
,gcc
,g++
,cc
,c++
, has been taken over by previously installed aarch64-linux-gnu-gcc
, you should always explicitly specify the prefix arm-linux-gnueabihf-
when using them.
To cross-compile hello-world as a 32-bit Linux application. On Fedora, only building kernels is currently supported. Support for cross-building user space programs is not currently provided as that would massively multiply the number of packages.
To cross-compile hello-world as a 64-bit Linux application. On Fedora, only building kernels is currently supported. Support for cross-building user space programs is not currently provided as that would massively multiply the number of packages.
test demos#
gas - asm#
# aarch64-linux-gnu-as && aarch64-linux-gnu-ld
$ as write64.s -o write64.o && ld write64.o -o write64
$ file write64
write64: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, not stripped
$ objdump -f write64
write64: file format elf64-littleaarch64
architecture: aarch64, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0000000000400078
$ ./write64
Hi A64!
$ arm-linux-gnueabihf-as write32.s -o write32.o && arm-linux-gnueabihf-ld write32.o -o write32
$ file write32
write32: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, not stripped
$ objdump -f write32
write32: file format elf32-littlearm
architecture: armv3m, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x00010054
$ readelf -h write32 | grep Flags
Flags: 0x5000200, Version5 EABI, soft-float ABI
$ ./write32
Hi A32!
gcc - c#
The equivalent C code of write32.s/write64.s is the following:
#include <unistd.h>
int main(int argc, char* argv[])
{
#if __ARM_ARCH_ISA_A64 // __ARM_64BIT_STATE
write(1, "Hi A64!\n", 8);
#else // __ARM_ARCH_ISA_ARM // __ARM_32BIT_STATE
write(1, "Hi A32!\n", 8);
#endif
return 0;
}
rpi3b-ubuntu/aarch64 naturally run in AArch64 state.
Compile the program with gcc
(symbolic link of aarch64-linux-gnu-gcc
):
$ gcc -x c -E -dM /dev/null | grep -E "__ARM_ARCH_ISA_|__ARM_(32|64)BIT_STATE"
#define __ARM_64BIT_STATE 1
#define __ARM_ARCH_ISA_A64 1
$ gcc write.c -o write64 && ./write64
Hi A64!
For the Arm 32-bit, we need to run the compatible GCC version.
$ arm-linux-gnueabihf-gcc -x c -E -dM /dev/null | grep -E "__ARM_ARCH_ISA_|__ARM_(32|64)BIT_STATE"
#define __ARM_ARCH_ISA_ARM 1
#define __ARM_32BIT_STATE 1
#define __ARM_ARCH_ISA_THUMB 2
Cross-compile the program with arm-linux-gnueabihf-gcc
as usual:
Attempt to run the DYN(dynamically linked) ELF32, it fails to interpret:
To run ARM ELF32 under Aarch64(A64), we have to resort to QEMU emulator. The emulator will interpret the ARM machine code and simulate it using the local processor.
Arm Assembly Internals and Reverse Engineering | Chapter 9 Arm Environments - Emulation with QEMU
Let's first install the following packages:
Then we can check the installed emulators for ARM and seek help:
Cross-compile with option -static
to create a static executable:
$ arm-linux-gnueabihf-gcc -static write.c -o swrite32
$ file swrite32
swrite32: ELF 32-bit LSB executable, ARM, EABI5 version 1 (GNU/Linux), statically linked, BuildID[sha1]=3befb9bf54c9686e132f66f3b261cf0e26c5c569, for GNU/Linux 3.2.0, not stripped
$ objdump -f swrite32
swrite32: file format elf32-littlearm
architecture: armv7, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x00010339
$ readelf -h swrite32 | grep Flags
Flags: 0x5000400, Version5 EABI, hard-float ABI
Run it on the AArch64 Linux/Ubuntu host using QEMU's user-mode emulation.
We can also run this binary directly from the command line without specifying qemu-arm-static
, just type ./swrite32
, it works as if by magic.
For dynamically linked executables, we can supply the path of the ELF interpreter and libraries via the option -L
(QEMU_LD_PREFIX).
$ arm-linux-gnueabihf-gcc write.c -o dwrite32
$ file dwrite32
dwrite32: ELF 32-bit LSB pie executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, BuildID[sha1]=1562e8ee664fa668df0743d4a77812400307b5d3, for GNU/Linux 3.2.0, not stripped
$ objdump -f dwrite32
dwrite32: file format elf32-littlearm
architecture: armv7, flags 0x00000150:
HAS_SYMS, DYNAMIC, D_PAGED
start address 0x00000409
$ qemu-arm -L /usr/arm-linux-gnueabihf/ ./dwrite32
Hi A32!
Behind the scene/Under the hood
Under the hood, QEMU can emulate an Arm processor by decoding and running each Arm instruction in software. System calls issued by the program are intercepted and sent to the host system, allowing the program to seamlessly interact with the rest of the system.
references#
radcolor prebuilt - arm-linux-gnueabi / aarch64-linux-gnu
What is the difference between arm-linux-gcc and arm-none-linux-gnueabi
20.04 - How to install "gcc-arm-linux-gnueabihf" specific version? - Ask Ubuntu
Debian/Ubuntu 安装交叉工具链:gcc-arm-linux-gnueabi,gcc-aarch64-linux-gnu
Embedded Programming with the GNU Toolchain - gnu-eprog@github
QEMU documentation, QemuUserEmulation
Emulating ARM with QEMU on Debian/Ubuntu: gist1, gist2
qemu-user-static/docs/developers_guide.md
基于QEMU和binfmt-misc透明运行不同架构程序
linux下使用binfmt_misc设定不同二进制的打开程序
qemu - What's the difference between "arm-linux-user" and "armeb-linux-user"?
linux - what is default qemu arm enviroment in qemu-arm-static?
What is binfmt_misc and how to enable/disable it?
Install binfmt for all cpus via qemu-user-static