Porting EmbCC to a new host#
This page is for people who want to run EmbCC on an operating system it does not support yet: a hobby OS, a non-POSIX OS, or any system with a C library and no GCC or Clang. It covers what EmbCC needs from the host, the platform layer you implement or choose, how the compiler finds its headers and libraries, and how to build it.
What EmbCC needs from the host#
The compiler and its linker run in one process. They never start another program, and they need no threads, signals, sockets or directory listings. On a host, EmbCC needs:
- A hosted ISO C library (C99) with the functions below.
- A way to start a program with arguments (
argc/argv). - A file system that
fopencan reach.
The compiler's sources include only ISO C headers: <stdio.h>,
<stdlib.h>, <string.h>, <ctype.h>, <stdarg.h>, <setjmp.h>,
<stddef.h>, <limits.h>, <stdint.h> and <math.h>. The one exception
is src/platform/platform_posix.c, which a POSIX host builds. Built with
PLATFORM=iso (below), the compiler calls only these C library functions:
| Header | Functions |
|---|---|
<stdio.h> |
fopen fclose fread fwrite fseek ftell rewind remove fgetc fputc fputs printf fprintf vfprintf snprintf vsnprintf sprintf sscanf, and stdout/stderr |
<stdlib.h> |
malloc calloc realloc free exit abort atexit getenv qsort atoi atol strtol strtoul strtoull strtod |
<string.h> |
memcpy memmove memset memcmp memchr strlen strcmp strncmp strcpy strncpy strcat strchr strrchr strstr strcspn |
<ctype.h> |
isalnum isalpha isdigit isspace isxdigit tolower toupper |
<setjmp.h> |
setjmp longjmp |
tests/golden/host-iso.sh builds the compiler this way and fails if it
references a POSIX call.
getenv may return NULL for every name: a host with no environment is
supported. The optional tools are a different matter. embdbg, embls
and embidx use POSIX (processes, sockets, terminals), and nothing in the
compiler depends on them.
The platform layer#
Everything EmbCC asks the host goes through src/platform/platform.h. No
other file in src/ may include a system header beyond the ISO C library
or test which host it runs on.
| Function | What it answers |
|---|---|
plat_read_file, plat_write_file, plat_file_exists |
Whole-file reads and writes, and whether a path can be opened |
plat_getenv |
An environment variable, or NULL |
plat_stderr_is_terminal |
Whether diagnostics may be coloured automatically |
plat_self_path |
The path of the running compiler, or NULL |
plat_set_argv0, plat_argv0_path |
argv[0], recorded by the driver at startup |
src_read, src_exists, src_set_provider |
Source files, which a language server can serve from its buffers |
The layer is split in three files:
platform_common.cholds the files, the environment,argv[0]and the source provider, in ISO C. Every host builds it.platform_posix.cis for macOS, Linux and EmbLinkOS. It usesisattyfor colour, and/proc/self/exeor_NSGetExecutablePathfor the compiler's own path.platform_iso.cis for every other host. Diagnostics are never coloured automatically (-fdiagnostics-colorstill works), and the compiler's path isargv[0]when that is a path.
The Makefile chooses with PLATFORM:
make embcc # PLATFORM=posix, the default
make embcc PLATFORM=iso # nothing but ISO C
A host that can answer more, such as one with a terminal API or a call
that returns the executable's path, gets its own platform_NAME.c beside
these, built with PLATFORM=NAME. It must define plat_stderr_is_terminal
and plat_self_path. Do not add #ifdefs to the existing files.
Finding headers and libraries#
The compiler looks for its support files, which are the C headers, the C++ headers and the per-target libraries, in this order:
EMBCC_PREFIXfrom the environment:$EMBCC_PREFIX/lib/embcc/VERSION.- The compiler's own path (
plat_self_path):- a build tree, when
lib/libc/include/stdio.his beside the binary; - otherwise an installed layout,
PREFIX/bin/embccwith the files inPREFIX/lib/embcc/VERSION.
- a build tree, when
- A prefix compiled in with
make DEFAULT_PREFIX=/path. This is used only when the host cannot say where the compiler is, for example with no environment and anargv[0]that is a bare name.
If none of these applies, the compiler uses only what its command line
gives it (-I, -isystem, -nostdinc). embcc --print-search-dirs shows
what was found:
layout: build tree
self: ./embcc
lib: .
Building EmbCC#
The source tree compiles with any C99 compiler, EmbCC included. The default
build uses make:
make embcc embld # the compiler and the linker
make install PREFIX=/opt/embcc
make install copies the programs to PREFIX/bin and the support files to
PREFIX/lib/embcc/VERSION. DESTDIR stages the copy elsewhere, as for
packaging.
The libraries are packaged with embar,
EmbCC's own archiver, which is ISO C like the compiler. Building EmbCC and
its libraries needs a C compiler and nothing else from a toolchain.
On a host with no make, compile every file of the SRCS list in the
Makefile (make -pn | sed -n 's/^SRCS := //p' prints it on a machine that
has make), plus build/embdbg_core.o from tools/embdbg/embdbg.c with
-DEMBDBG_NO_MAIN, and link them into one program. EmbLinkOS does exactly
this from a generated manifest, build.ebm (see
EmbBuild manifests).
Building EmbCC for an OS that has no compiler yet#
Use EmbCC on another machine. tools/cross-embcc.sh compiles every source
of the compiler with ./embcc --target=TRIPLE, using the ISO C host layer.
It then links the objects with ./embld, the OS's startup object and its
C library. Nothing from GCC, Clang or binutils takes part.
make embcc embld embar
CROSS_CFLAGS="-O1 -nostdinc -isystem /path/to/os/include" \
CROSS_CRT=/path/to/os/lib/crt0.o \
CROSS_LIBS="/path/to/os/lib/libc.a" \
CROSS_LDFLAGS="-e _start -Ttext 0x400000" \
sh tools/cross-embcc.sh x86_64-elf embcc-myos
| Variable | What it gives |
|---|---|
CROSS_CFLAGS |
Compile flags, chiefly where the OS's C headers are. The default is -O1. |
CROSS_CRT |
The OS's program startup object. |
CROSS_LIBS |
Its C library and anything that needs, in link order. |
CROSS_LDFLAGS |
embld options for the OS's program loader: entry point and load address. |
CROSS_OBJ |
Where the objects go. The default is build/cross-TRIPLE. |
For x86_64-linux-gnu and aarch64-linux-gnu the defaults are EmbCC's own
C library (make libc-linux-x86_64), so no variables are needed:
sh tools/cross-embcc.sh x86_64-linux-gnu embcc-linux
tests/golden/cross-embcc.sh tests this end to end. It builds EmbCC for
Linux this way and boots it on a Linux kernel under QEMU, with the headers
in the root file system. There it compiles a program that includes
<stdio.h>, and the assembly must be identical to what the host's EmbCC
emits.
To use the result on the new OS, copy it there with the support files, in the build-tree layout:
/path/embcc
/path/lib/libc/include/ from lib/libc/include
/path/include/ from include (stddef.h, stdarg.h, ...)
The compiler finds them from argv[0]. Otherwise, install them as
PREFIX/lib/embcc/VERSION and set EMBCC_PREFIX, or build with
DEFAULT_PREFIX. Add the target libraries the same way for the targets
you link for.
EmbLinkOS is built along the same lines. Its C library is newlib, and its
embcc.elf comes from build.ebm (see
EmbBuild manifests).