EmbCCdocs Repository

Overview#

This page describes what EmbCC is: the programs that make up the toolchain and how they fit together, the targets it generates code for, and the language levels it accepts. It is for anyone meeting EmbCC for the first time, and it points to the page that covers each subject in full. To build EmbCC and compile a first program, go on to Getting started.

What EmbCC is#

EmbCC is a compiler for C and C++ for embedded systems. Together with its own preprocessor, assemblers, linker, debugger, simulator and supporting tools, it makes up the Emb toolchain. It produces code for:

  • Arm Cortex-M microcontrollers (ARMv6-M, ARMv7-M, ARMv7E-M with DSP, and ARMv8-M Baseline and Mainline with TrustZone-M), and ARMv7-A;
  • RISC-V, RV32 and RV64, with the F and D extensions;
  • AVR (ATmega);
  • Xtensa (ESP32 class), Infineon TriCore, Renesas RX, NXP ColdFire, MIPS32 and MIPS64, PowerPC, SPARC V8 (LEON3) and LoongArch64;
  • operating systems and hosted programs: bare-metal x86-64 and AArch64 (EmbLinkOS among them), Linux on x86-64 and AArch64, macOS (Mach-O) and Windows (COFF), with the limits listed under Targets at a glance.

EmbCC runs on macOS and Linux. It is written in C99, and it compiles its own sources.

One embcc binary contains every code generator. --target=TRIPLE selects the target for one invocation; without it, EmbCC compiles for x86_64-elf, unless the compiler was built with make DEFAULT_TARGET=TRIPLE or the environment sets EMBCC_DEFAULT_TARGET. See Targets.

embcc starts no other program. Preprocessing, compiling, assembling and, where the driver links, linking all run inside the one embcc process, so compiling needs no assembler or linker from another toolchain on the host.

embcc --version prints the version and the default target on its first line:

EmbCC 0.0.1-beta (the Emb toolchain's C and C++ compiler, for embedded systems and operating systems)

Components#

Program What it does Reference
embcc The compiler driver. Preprocesses, compiles C and C++, assembles .s, .S and .asm files, writes ELF, Mach-O or COFF objects with DWARF debug information, and links x86-64 ELF programs and ARM, RISC-V, MIPS32 and AVR firmware with the linker built into it. Invoking EmbCC
embas The standalone assembler for x86-64 in NASM (Intel) syntax. Writes ELF64 objects. embcc -c FILE.asm runs the same assembler. embas
embld The static linker. Links ELF objects and archives for x86-64, Cortex-M, RV32, RV64, MIPS32 and AVR into an ELF executable, or, with --embx, into an EMBX image for EmbLinkOS. embld --doctor explains why a link fails. embld
embread Prints an EMBX image and checks it against the rules the EmbLinkOS loader applies. embread
embdbg The debugger. Symbolizes addresses, lists functions, lines and variables, analyzes crash reports, disassembles, and debugs a running target through the GDB remote protocol. Reads DWARF and EmbCC's .embdbg files; does not need gdb. embdbg
embls A language server (LSP over standard input and output). Diagnostics come from running embcc; completion, hover, go-to-definition and document symbols come from EmbCC's own preprocessor and parser. embls
embidx A cross-unit index built from embcc --emit-interfaces. Its commands are build, stale (which units must be rebuilt), check (declarations that two units saw differently) and who (where a declaration is defined and used). embidx
EmbBuild manifests build.ebm at the top of the source tree is the EmbBuild manifest that builds EmbCC on EmbLinkOS. tools/gen-embbuild-manifest.sh generates it; tools/embbuild-run.sh executes a manifest on the host. EmbBuild manifests

make all builds embcc, embread, embld, embas, embls and embidx. embdbg is a separate target, make embdbg. See Building from source.

EmbCC also provides the libraries a program links against:

Directory Contents
include/ EmbCC's freestanding headers, among them <stddef.h>, <stdarg.h>, <stdint.h>, <stdbool.h>, <limits.h> and <float.h>.
lib/libc EmbCC's C library, for EmbLinkOS, bare-metal x86-64 and AArch64, and static Linux programs.
lib/libcxx EmbCC's C++ runtime and standard library.
lib/rt The compiler runtime: the routines generated code calls for operations the target has no instruction for, such as soft-float arithmetic and wide division.

The compiler finds its headers and libraries relative to its own location, in a build tree or an installation; embcc --print-search-dirs shows what it found. See Libraries.

How the pieces fit together#

Inside embcc#

embcc compiles exactly one input file per invocation. What it does with the file depends on the file name suffix (see Input files):

.c                      preprocessor -> parser -> semantic analysis
.cc .cpp .cxx .C ...    preprocessor -> C++ front end, which lowers the
                        unit to C -> parser -> semantic analysis
                                 |
                                 v
                        EmbIR -> optimizer -> code generator for the
                        target -> ELF, Mach-O or COFF object (+ DWARF)

.s .S                   GNU-syntax assembler (.S is preprocessed first);
                        AArch64, Cortex-M, RISC-V and AVR targets
.asm                    NASM-syntax assembler shared with embas; x86-64

Without -c, -S or -E, embcc compiles the file and then links it in the same process, adding the target's start-up file and libraries. The driver links the x86-64 ELF targets, and ARM, RISC-V, MIPS32 and AVR firmware given its memory map (--link-script=board.ld, or -Wl,-Ttext/-Tdata):

embcc --target=thumbv7em-none-eabi --link-script=board.ld startup.o main.o -o fw.elf

For AArch64 ELF, macOS and Windows targets it stops with embcc: error: cannot link for TRIPLE; compile those with -c and link with another toolchain's linker, because embld does not read their objects.

embcc inspect STAGE FILE prints one stage of this pipeline (tokens, preprocessed source, syntax tree, symbols, struct layout, EmbIR, control flow graph or call graph) instead of producing an object, and embcc why reports an optimizer decision. See Asking EmbCC. The design of each stage is described in Architecture, Front end, EmbIR, Optimizer and Back ends.

Between the programs#

source files --embcc -c--> objects (.o) --+
.asm files ----embas-----> objects (.o) --+
                                          |
                    archives (.a) --------+--> embld --> ELF executable
                                                 |         + FILE.embdbg when an
                                                 |           object has -g info
                                                 |
                                                 +--> --embx: EMBX image
                                                              |
                                                              v
                                                           embread

ELF executable, FILE.embdbg, crash report --> embdbg
editor <--LSP--> embls --runs--> embcc -fsyntax-only
embidx --runs--> embcc --emit-interfaces --> index file

A firmware image for a Cortex-M part, for example, is built by compiling each file with embcc -c and linking the objects with embld:

embcc --target=thumbv7m-none-eabi -c boot.c -o boot.o
embcc --target=thumbv7m-none-eabi --opt=speed -g -c main.c -o main.o
embld -e reset -Ttext 0x0 -Tdata 0x20000000 boot.o main.o -o fw.elf

Because main.o carries debug information, embld also writes fw.elf.embdbg, which embdbg reads. Start-up code, linker addresses and interrupt handlers are covered in Embedded programming; debugging in Debugging.

Targets at a glance#

Every target is little-endian. The triples listed are the canonical spellings; Targets lists the accepted aliases and every detail of each target's ABI, options and predefined macros.

Target Canonical triples Object format Data model Status
x86-64, bare metal and EmbLinkOS x86_64-elf, x86_64-emblink ELF64 LP64 Primary target. embcc compiles and links; embld --embx produces EmbLinkOS images.
x86-64 Linux x86_64-linux-gnu ELF64 LP64 Static executables on EmbCC's own C library, linked by embcc.
AArch64, bare metal, EmbLinkOS and Linux aarch64-elf, aarch64-emblink, aarch64-linux-gnu ELF64 LP64 Compiles to objects; link with another toolchain's linker.
ARM Cortex-M thumbv7m-none-eabi, thumbv7em-none-eabi, thumbv7em-none-eabihf, thumbv8m.main-none-eabi, thumbv8m.main-none-eabihf ELF32 ILP32 Bare metal; linked by embld. Soft float; on ARMv7E-M and ARMv8-M, optionally the single-precision FPU.
RISC-V RV32 riscv32-unknown-elf ELF32 ILP32 Bare metal, RV32IMAC; linked by embld.
RISC-V RV64 riscv64-unknown-elf ELF64 LP64 Bare metal, RV64IMAC; linked by embld.
AVR avr ELF32 16-bit int and pointers, 32-bit long, 4-byte double Bare metal, ATmega328P; linked by embld.
macOS x86_64-apple-darwin, aarch64-apple-darwin Mach-O LP64 Objects for the system linker. -g and __thread are refused.
Windows x86_64-windows-gnu COFF LP64 Objects only, not yet the Microsoft x64 ABI; every compile warns (--warn=windows-abi). No C library.

x86_64-elf and x86_64-emblink produce the same code and differ only in their predefined macros: x86_64-emblink defines __emblink__, __emblink and __EmbLinkOS__. The data models differ in more than the sizes of long and pointers: plain char is unsigned on AArch64, Cortex-M and RISC-V, and long double has a different format on almost every target. The full table is in Data models. Windows keeps long at 8 bytes, where the Microsoft ABI has 4.

An unknown triple is an error that lists every triple EmbCC accepts:

embcc: error: unknown target 'armv6m-none-eabi'
embcc: the targets it emits for are:
embcc:   x86_64-elf
...

Language levels at a glance#

Language What EmbCC parses --std= Predefined level
C One dialect: C11 with the GNU extensions, plus C23 features such as nullptr, [[...]] attributes, auto type inference, enumerations with a fixed underlying type, and #embed Every C standard name from c89 to gnu23 is accepted and changes nothing; one older than C11 draws a warning __STDC_VERSION__ is 201710L
C++ C++20 with GNU extensions and some C++23 features, compiled by lowering it to C c++98 to c++26 and the gnu++ forms set __cplusplus and the feature-test macros; the parser still reads C++20 __cplusplus is 202002L by default (-std=gnu++20)

--std= never makes EmbCC reject a construct of a newer standard. The exact list of accepted names is under --std=STANDARD.

C is supported on every target. C++ is supported on the x86-64 and AArch64 targets, with restrictions on macOS and Windows. On the 32-bit embedded targets (Cortex-M and ARM state, RV32, MIPS, PowerPC, SPARC, ColdFire, Xtensa, TriCore and RX) it is supported without exceptions, with a small runtime (make libcxx-embedded). On RV64, little-endian MIPS64 and LoongArch64 a C++ unit compiles with --no-exceptions, but C++ is not supported or tested there. On AVR EmbCC refuses to generate code for a C++ unit (embcc: error: C++ is not yet supported for avr: ...); --syntax-only still checks one. The exact list is under Targets in the C++ page.

The language pages give the details:

Where to go next#

For people compiling code with EmbCC:

  • Getting started: building and installing EmbCC, a first program, and cross-compiling for each board.
  • Invoking EmbCC: every command-line option.
  • Build systems: the CMake toolchain file, and the GCC names (arm-none-eabi-gcc, -objcopy, -size...) for a project whose build is written for a GCC toolchain.
  • Targets: each target's triples, options, ABI, predefined macros and limitations.
  • Embedded programming: start-up code, interrupts, memory-mapped I/O and linking firmware.
  • Libraries: the C library, the C++ library and the compiler runtime.
  • Diagnostics: the message format, warnings, --explain, JSON output and fix-its.
  • Optimization: the -O levels and the -f options.
  • Debugging: -g, the DWARF EmbCC emits, and the debuggers that read it.
  • The tool reference pages: embas, embld, embread, embdbg, embls, embidx and EmbBuild manifests.

For people changing EmbCC, start with Architecture, then Contributing and Testing. Decisions records the design decisions, and Status what is complete and what is not.