GNU Assembler
The GNU Project assembler (GAS, invoked as "as"), the back-end assembler for GCC and a portable, multi-architecture assembler distributed as part of GNU Binutils.
Created by Dean Elsner (original author); maintained by the GNU Project / Free Software Foundation
The GNU Assembler, commonly known as GAS and invoked on the command line as as, is the assembler distributed by the GNU Project as part of the GNU Binutils package. It serves as the back-end assembler for the GNU Compiler Collection (GCC): when GCC compiles a source file to assembly, it is GAS that translates that assembly into machine code. GAS is unusual among assemblers in being deliberately multi-architecture – a single codebase, configured at build time for a target triple, supports dozens of instruction set architectures including x86, x86-64, ARM, AArch64, RISC-V, MIPS, PowerPC, SPARC, s390, and many others. By default GAS uses AT&T syntax for x86 targets, though it also accepts Intel syntax via the .intel_syntax directive.
History & Origins
The GNU Toolchain Vision
The GNU Assembler emerged from Richard Stallman’s broader project, announced in 1983, to build a complete free Unix-compatible operating system. A working compiler toolchain – compiler, assembler, linker, and supporting utilities – was a prerequisite for that goal. GCC handled the compiler role, but it required a free assembler that could turn its output into object files without depending on proprietary Unix tools like AT&T’s as.
Dean Elsner is credited as the original author of GAS, with work beginning around 1986 at the Free Software Foundation. The early assembler targeted the architectures most relevant to the GNU project at the time – the VAX and Motorola 68000 families that dominated Unix workstations – before expanding to additional targets as the GNU toolchain spread to new platforms. Many subsequent maintainers, including Jay Fenlason, Ken Raeburn, Steve Chamberlain, Ian Lance Taylor, Alan Modra, and Nick Clifton, shaped GAS into the multi-architecture assembler it is today.
Integration into Binutils
In the early 1990s the GNU assembler, linker, and binary utilities were unified into a single package, GNU Binutils. This consolidation simplified distribution and made it possible to share a common library – the Binary File Descriptor (BFD) library – across the entire toolchain. BFD abstracts the differences between object file formats (a.out, COFF, ELF, Mach-O, PE) and allows GAS, ld, objdump, objcopy, nm, and other tools to operate uniformly across formats and architectures.
The result is a remarkably portable assembler. The same source tree, configured with different --target options, can produce an assembler for x86-64 Linux, ARM bare-metal firmware, a PowerPC AIX target, or a RISC-V embedded system.
Design Philosophy
One Assembler, Many Targets
Where assemblers such as MASM and NASM are designed around a single architecture (x86), GAS is built around a target-independent core. The core handles lexical analysis, the pseudo-op directives, symbol tables, sections, relocations, and macro processing; an architecture-specific back-end (in gas/config/tc-<arch>.c) plugs in the instruction encoding and target-specific syntax.
This design has costs and benefits:
- Benefit: A single, maintained codebase delivers reliable assemblers for an enormous range of architectures, including obscure or legacy targets that would otherwise be unsupported.
- Cost: GAS is not the most ergonomic assembler for any single architecture. NASM and FASM, for example, often have cleaner Intel-syntax tooling for x86 work, and Microsoft’s MASM has deeper integration with the Windows ABI.
AT&T Syntax by Default
For x86 and x86-64 targets, GAS defaults to AT&T syntax, which originated at AT&T Bell Labs for Unix assemblers and differs noticeably from Intel’s documented syntax:
- Operand order is source first, destination last, the reverse of Intel syntax.
- Registers are prefixed with
%, immediates with$, and memory references use parentheses:movl $42, %eax,addl 8(%ebx), %eax. - Mnemonics carry size suffixes:
b(byte),w(word, 16-bit),l(long, 32-bit),q(quad, 64-bit) – e.g.,movq,addl,movb. - Memory addressing uses the form
disp(base, index, scale)rather than Intel’s[base + index*scale + disp].
GAS also supports Intel syntax via the .intel_syntax noprefix directive, which is commonly used by developers porting code originally written for MASM or NASM.
Pseudo-Ops and Directives
A large part of GAS is its set of pseudo-operations (directives that begin with a period) that control sections, alignment, symbol visibility, debug info, and macro expansion. Examples include:
.text,.data,.bss,.section– section selection.globl,.local,.weak,.hidden– symbol attributes.byte,.word,.long,.quad,.ascii,.asciz– data emission.align,.balign,.p2align– alignment.macro/.endm,.if/.else/.endif,.include– macro and conditional assembly.cfi_startproc/.cfi_endprocand related CFI directives – DWARF unwinding information
These directives are largely consistent across architectures, which makes basic GAS source files quite portable – only the actual instruction mnemonics tend to differ between targets.
Key Features
Macro System
GAS supports a relatively powerful macro facility through .macro/.endm, with parameter substitution, default values, and conditional assembly. For example:
| |
While not as elaborate as MASM’s high-level macro system or NASM’s %macro constructs, the GAS macro system is sufficient for the kinds of repetitive patterns that arise in operating-system and library code.
Architecture Coverage
GAS supports an exceptionally broad set of targets. A non-exhaustive list of architectures with first-class support in recent Binutils releases includes:
| Family | Examples |
|---|---|
| x86 | i386, x86-64, x32 |
| ARM | ARMv4-ARMv8 (32-bit), AArch64 (ARMv8-A and later) |
| RISC-V | RV32, RV64, with standard extensions (I, M, A, F, D, C, V) |
| PowerPC | 32-bit PowerPC, PowerPC64 (BE and LE), POWER ISA |
| MIPS | MIPS I-V, MIPS32, MIPS64, microMIPS |
| s390 | IBM z/Architecture (s390, s390x) |
| SPARC | SPARC V8, V9 |
| Other | m68k, SH, ARC, AVR, MSP430, Xtensa, LoongArch, and many others |
For some legacy or specialty targets (e.g., older Motorola, Hitachi, or NEC chips), GAS is one of the few maintained free assemblers still in active use.
Integration with GCC
A typical gcc hello.c -o hello invocation runs three programs in sequence: the compiler proper (cc1), the assembler (as = GAS), and the linker (ld). The assembly that cc1 produces is hand-tailored to GAS’s syntax and directives; this tight coupling is one of the reasons GAS and GCC versions are usually upgraded together as a matched toolchain. The gcc -S option stops the compilation pipeline after producing assembly, exposing the input GAS would normally consume.
Evolution
From a.out to ELF
Early GAS releases targeted the a.out object file format used by BSD Unix and early System V variants. Through the 1990s, most Unix and Unix-like systems migrated to ELF (Executable and Linkable Format), and GAS followed – ELF is today the default object format for the great majority of GAS targets, with BFD providing transparent support for COFF (Windows PE-COFF via the MinGW toolchain), Mach-O (older macOS toolchains), and other formats where required.
New Architectures
The introduction of new architectures has been a recurring milestone in GAS’s history. Notable additions include x86-64 around 2003 (tracking AMD’s AMD64 launch), AArch64 around the Binutils 2.23 release in October 2012, RISC-V upstreamed in 2017 with Binutils 2.28, and LoongArch in the early 2020s. Because GAS is the canonical assembler for the GNU toolchain, “upstreamed into Binutils” is often the moment a new architecture is considered to have arrived in the mainstream open-source ecosystem.
Pace of Releases
GNU Binutils – and therefore GAS – has followed a roughly annual release cadence in recent years, with point releases for bug fixes. Releases are coordinated on the binutils mailing list; Nick Clifton has been a long-running release manager. Each release typically brings new instruction support for recent CPU extensions (for example, x86 AVX-512, ARM SVE, RISC-V vector extensions) and incremental improvements to existing back-ends.
Current Relevance
GAS occupies a load-bearing position in the open-source software ecosystem:
- It is the default assembler for GCC, which in turn builds most of the user-space and kernel code shipped in Linux distributions. Every glibc release, every Linux kernel build, and every distribution’s package archive flows through GAS at build time on the relevant architectures.
- It underpins cross-compilation. Embedded developers building firmware for ARM Cortex-M, RISC-V microcontrollers, or PowerPC SoCs typically use a cross-compiled GCC + Binutils toolchain, where GAS is the assembler component.
- It is the de facto reference for AT&T-syntax x86 assembly taught in many Unix-oriented systems courses and used by developers who hand-write inline assembly in GCC’s
asm()blocks.
Alternatives exist – LLVM’s integrated assembler is now used by Clang for many targets, often replacing GAS in the LLVM toolchain – but GAS remains the canonical assembler for GCC-centric workflows and continues to be the first-class assembler for many architectures that LLVM either supports less completely or not at all.
Why It Matters
The GNU Assembler is not famous in the way that GCC, the Linux kernel, or Git are famous, but its quiet ubiquity is part of what makes it important. Almost every program compiled by GCC – which is to say almost every program in a typical Linux distribution – has at some point in its build passed through GAS. The assembler is, in effect, the layer where the GNU toolchain’s promise of free software meets the silicon: it is the piece of software that turns human- or compiler-written assembly text into the bytes that the processor actually executes.
GAS also embodies a particular bet about how assemblers should be built. Rather than building dozens of separate, architecture-specific tools, the GNU Project built one assembler with pluggable back-ends, and that bet has paid off across decades of new architectures. When RISC-V appeared, when AArch64 appeared, when LoongArch appeared, the path to mainstream toolchain support ran through GAS and the rest of Binutils. For programmers working close to the hardware on free software systems, GAS is the assembler – often the only assembler – they ever need to learn.
Timeline
Notable Uses & Legacy
GCC (GNU Compiler Collection)
GCC emits assembly source that is consumed by GAS to produce object files. On nearly every GNU/Linux system, every C, C++, Fortran, Ada, or Go program built with GCC passes through GAS as part of the standard compilation pipeline.
Linux Kernel
The Linux kernel build system uses GAS to assemble architecture-specific assembly sources -- boot code, context switching, interrupt handlers, and CPU feature probing -- across x86, x86-64, ARM, AArch64, PowerPC, RISC-V, MIPS, s390, and other supported architectures.
GNU C Library (glibc)
glibc contains hand-written assembly implementations of performance-critical functions (memcpy, memset, strlen, and math routines) for multiple architectures, all assembled with GAS.
Cygwin and MinGW Toolchains
The Cygwin and MinGW/MinGW-w64 projects ship GAS as part of their GCC-based toolchains, providing GNU-style assembly support on Windows for both 32-bit and 64-bit targets.
Cross-compilation Toolchains
Embedded and operating-system development relies on cross-compiled Binutils packages (e.g., arm-none-eabi-as, aarch64-linux-gnu-as, riscv64-unknown-elf-as) where GAS is the assembler component for building firmware, bootloaders, and bare-metal software.
Language Influence
Influenced By
Influenced
Running Today
Run examples using the official Docker image:
docker pull gcc:latestExample usage:
docker run --rm -v $(pwd):/src -w /src gcc:latest sh -c 'as -o hello.o hello.s && ld -o hello hello.o && ./hello'