EmbCCdocs Repository

Build systems: CMake, Make and GCC toolchain files#

A project usually reaches its compiler through a build system: a CMake toolchain file, or a Makefile that names arm-none-eabi-gcc, -ar, -objcopy and -size. EmbCC answers all of those names. There are two ways to use it:

  • The CMake toolchain file EmbCC ships, for a new project or one you are moving to EmbCC.
  • The GCC names, for a project whose build is written for a GCC cross toolchain. Install the names, put them first in PATH, and change nothing else.

The CMake toolchain file#

cmake -B build -G Ninja \
    -DCMAKE_TOOLCHAIN_FILE=/usr/local/share/embcc/cmake/embcc.cmake \
    -DEMBCC_TARGET=thumbv7em-none-eabihf \
    "-DEMBCC_FLAGS=-mcpu=cortex-m4 -Os"
cmake --build build
Variable Meaning
EMBCC_TARGET The triple, as embcc --target= takes it. Without it, the compiler's default target.
EMBCC_FLAGS Flags for every compile and link: a CPU, an FPU, an optimization level.
EMBCC_LIBC For a Linux triple: system builds against the system's glibc, with its POSIX headers and threads (--libc=system); embcc or empty, EmbCC's own C library. CMAKE_SYSROOT is then the system's root (--sysroot=).

A POSIX program for Linux, threads included, as CMake builds one with GCC:

cmake -B build -DCMAKE_TOOLCHAIN_FILE=<prefix>/share/embcc/cmake/embcc.cmake \
    -DEMBCC_TARGET=x86_64-linux-gnu -DEMBCC_LIBC=system

find_package(Threads) then finds glibc's (CMAKE_HAVE_LIBC_PTHREAD), and Threads::Threads is -pthread.

The file sets:

  • the C, C++ and assembler compilers to embcc, identified to CMake as EmbCC (The C compiler identification is EmbCC 0.0.1, CMAKE_C_COMPILER_ID is EmbCC), with EmbCC's rules beside it in Compiler/EmbCC-*.cmake: the flags for each build type (MinSizeRel is -Os), each C_STANDARD and CXX_STANDARD (C17 and C++20 when none is set), header dependencies and the archiver. CMake identifies a compiler by building a test file against the compilers it knows, and EmbCC is not one of them, so without this file it says "identification is unknown" and applies no rules for it. POSITION_INDEPENDENT_CODE and shared libraries reach EmbCC's refusal (-fPIC is not supported) rather than building ordinary code;
  • CMAKE_SYSTEM_NAME from the triple: Generic for a bare-metal triple (CMake's compiler checks then build a static library, not a program), Linux, Darwin or Windows otherwise;
  • CMAKE_AR to embar, which writes the symbol index as it writes the archive, so there is no separate ranlib step;
  • CMAKE_OBJCOPY, CMAKE_SIZE and CMAKE_NM to embcc-objcopy, embcc-size and embcc-nm;
  • header dependencies: every compile writes a -MD dependency file, so editing a header rebuilds the sources that include it.

Checked with CMake 3.25, 3.31 and 4.4.

It finds the tools next to itself: in an installation it is in share/embcc/cmake/ and they are in bin/; in a source tree it is in cmake/ and they are at the top (make builds the embcc-* names there).

A post-build step written for binutils works as it is:

add_custom_command(TARGET app POST_BUILD
  COMMAND ${CMAKE_OBJCOPY} -O ihex -R .eeprom $<TARGET_FILE:app> app.hex
  COMMAND ${CMAKE_SIZE} --format=berkeley $<TARGET_FILE:app>)

The GCC names#

make install-gnu PREFIX=/opt/embcc TRIPLES="arm-none-eabi riscv64-unknown-elf"
export PATH=/opt/embcc/bin:$PATH

For each triple this links <triple>-gcc and <triple>-cc to embcc, -ar and -ranlib to embar, -objcopy to embpack, and -size and -nm to embmap. The default TRIPLES are arm-none-eabi, riscv64-unknown-elf, riscv32-unknown-elf and avr, less those whose family the compiler was built without (Choosing the targets).

Called by such a name, embcc compiles for the triple in it, as clang does. A version after the name is skipped (arm-none-eabi-gcc-13.2), and --target= on the command line still decides. An unknown triple is refused:

embcc: error: called as 'bogus-thing-gcc', and 'bogus-thing' is not a target EmbCC knows; they are:

riscv64-unknown-elf-gcc builds RV32 code under -march=rv32..., as GCC's multilib toolchain of that name does. The other way round is refused: a riscv32 triple is RV32 only, and so is a hosted one of either width.

One thing a CMake toolchain file written for GCC must say: the archiver. CMake derives <prefix>-ar and <prefix>-ranlib from the compiler's name only for a compiler it identifies as GCC or Clang, and through the GCC names it does not identify EmbCC (which does not define __GNUC__; EmbCC's own toolchain file names it instead). It then uses the system ar, which on Linux handles ELF archives, and on macOS does not: Apple's ranlib drops the ELF members. So name them, as many arm-none-eabi toolchain files already do:

set(CMAKE_AR arm-none-eabi-ar)
set(CMAKE_RANLIB arm-none-eabi-ranlib)

A Makefile that names $(PREFIX)ar needs nothing.

What each binutils name does:

Name Program What it takes
-ar embar r, q, c, s, t, x, d (and D, u, v, o, which change nothing: the output is always deterministic). ELF members are archived in the System V / GNU format; Mach-O members in the BSD format with a __.SYMDEF table, which is what Apple's linker reads
-ranlib embar ranlib ARCHIVE...: each archive's symbol index, written again
-objcopy embpack -O binary, -O ihex, -O srec, -R SECTION, -j SECTION (both with * and ? patterns), --gap-fill BYTE, --pad-to ADDRESS, -I elf.... -S, -g and --strip-* are accepted and change nothing in an image.
-size embmap Berkeley (the default, -B) and SysV (-A) tables, -d, -o, -x, -t
-nm embmap -S, -g, -u, --defined-only, -n, -p, -r

The same programs answer to embcc-ar, embcc-ranlib, embcc-objcopy, embcc-size and embcc-nm, which make install always installs.

What they do not do:

  • objcopy writes images. An ELF-to-ELF copy (no -O, or -O elf32-...) is refused by name.
  • size and nm read ELF files, not archives.

Linking#

A bare-metal link needs a memory map: a linker script (--link-script=FILE.ld, -TFILE.ld) or -Wl,-Ttext= and -Wl,-Tdata=. GNU ld scripts are read by embld; see embld. --specs=nano.specs and --specs=nosys.specs are accepted and ignored, because EmbCC's C library is already small and its system calls are weak defaults (see Libraries).

On macOS a program is linked by Apple's linker; see Getting started.