C/C++ on Motor OS
Rust is the preferred language on Motor OS, but C and C++ are fully supported. Motor OS has a working C library (mlibc), the LLVM C++ runtime (libc++, libc++abi, libunwind), and a Clang/LLVM toolchain that targets Motor OS directly. The developer image compiles C and C++ natively, the same toolchain cross-compiles on Linux, and Lua ships on the developer image as a stock C program built with it.
How it fits together
C or C++ program
libc++ / libc++abi / libunwind (C++ only)
mlibc: stdio, malloc, pthreads, sockets, dirent, poll, locale, ...
Motor OS sysdeps (mlibc's OS layer, written for Motor)
libmoto_rt_cabi: a C ABI over the Motor runtime (<moto_rt.h>)
rt.vdso -> kernel / sys-io
mlibc is a portable C library also used by managarm and other operating systems. It
was chosen because it already provides what real programs and compilers need,
pthreads, a POSIX-shaped filesystem API, posix_spawn, sockets, and locales,
so the port consists of an OS layer for Motor and a C ABI shim over the same runtime that
Rust's standard library uses. The C library is built with mlibc's ANSI and POSIX option
groups. Its runtime configuration, resolv.conf and hosts, lives in
/system/cfg/libc, which sys-io maintains and which every image carries, so a
C program runs on the standard image too, not only on the developer image.
The target
x86_64-unknown-motor is a real Clang target, not a set of flags to remember:
- Programs are static position-independent executables. There is no dynamic linking
on Motor OS, so there is no
dlopen;-lm,-lpthread,-lrt,-ldl, and similar link names are accepted for compatibility with existing build systems, and the code is in the one staticlibc.a. - Thread-local storage is emulated TLS, on by default, implemented over the runtime's
TLS API.
thread_localvariables and their destructors work. - The linker is lld, the builtins come from compiler-rt, and the C++ standard library
is libc++. The Clang driver owns the link line (
crt1.o, the runtime shim, libc++abi, libunwind, libc, the builtins), socc hello.cneeds no options;-nostartfilesand-nodefaultlibsare honored for code that wants to bring its own. - The only platform macros are
__motor__and__ELF__. There is no__linux__and no__unix__, so code that dispatches on them takes its generic POSIX branch, which is usually the right one.
mlibc is implemented in C++ internally, so even a pure C program links libc++abi and libunwind; the driver does this automatically.
What is supported
| Area | Supported |
|---|---|
| C standard library | stdio including memory streams, printf/scanf with correct float round trips, stdlib, string, math, locale, wide characters, setjmp/longjmp, abort (exit status 134), atexit. |
| Memory | malloc and friends, aligned_alloc, posix_memalign, anonymous mmap. |
| Files and directories | open, read, write, lseek, stat/fstat, dirent, getcwd/chdir, mkdir, rename, unlink, ftruncate, fsync, temporary files and directories, isatty. Permissions follow the three-role model (see Filesystem). |
| Threads | pthreads: threads, mutexes, condition variables, read-write locks, pthread_once, keys with destructors; thread-local storage. |
| Networking | BSD sockets over TCP and UDP, IPv4 and IPv6; getaddrinfo using the same resolv.conf and hosts files as the Motor DNS service; poll and select, bridged onto the runtime's readiness interface. |
| Processes | posix_spawn, waitpid, system and popen (through /system/bin/sh, which is rush), getenv, getpid, exit codes. |
| Signals | Synchronous only: signal, sigaction, and sigprocmask record dispositions, raise runs the handler in the calling thread, kill terminates a child. Nothing is delivered asynchronously; Ctrl+C is handled as described under Terminals. |
| Time | clock_gettime with real-time and monotonic clocks, nanosleep, strftime. |
| C++ | libc++ with exceptions and RTTI on; <iostream>, containers, <thread>, <filesystem>, <random>, <chrono>, locales, wide characters, aligned new, static constructors and destructors in the right order. |
| Motor-specific calls | <moto_rt.h> exposes the runtime's C ABI (files, sockets, memory, futexes, arguments and environment, random bytes, logging, terminal status) for programs that need something POSIX does not express. |
Differences from Unix, by design
Motor OS is not Unix, and the C library does not pretend otherwise. The differences below are deliberate properties of the system, described in detail on the Processes, Filesystem, and Terminals pages; for a C program they mean:
- There is no
fork. Start children withposix_spawn,system, orpopen. - Signals are not delivered asynchronously; there are no process groups and no
SIGCHLD. Usewaitpidandpoll. - There are no symbolic or hard links, no users or groups (
getuidand friends are not meaningful), and file permissions are per role rather than per owner. - There is no file-backed
mmap; read the file. Anonymous mappings work. - A terminal is a pipe whose far end behaves like one:
isattyworks, but there is no termios and noioctl. Raw mode and the terminal size are handled by the terminal provider, as described under Terminals. - Everything is statically linked; there is no
dlopen.
Building
On the developer image
cc and c++ in /devtools/bin are short scripts over
the LLVM multicall binary in /devtools/llvm/bin; headers are under
/devtools/llvm/include and the libraries under /devtools/llvm/lib.
The samples in /devtools/src build like this:
cd /devtools/src
cc -O2 hello.c -o /devtools/tmp/hello && /devtools/tmp/hello
c++ -O2 hello.cpp -o /devtools/tmp/hello-cpp && /devtools/tmp/hello-cpp
A freshly linked program is executable by its creator and by higher roles, so it can
be run at once. The developer image has no make; small projects are built from
a rush script or by listing the sources on one cc line. Compiler temporary
files go to /devtools/tmp.
On Linux
The Motor OS build produces the same Clang for the Linux host together with a
sysroot and two wrappers, motor-clang and motor-clang++, that
run it with --target=x86_64-unknown-motor and the sysroot:
motor-clang -O2 hello.c -o hello
motor-clang++ -O2 hello.cpp -o hello-cpp
Because the result is static, it runs on any Motor OS image; copy it in with
scp as in Hello Motor OS. The host toolchain and the
wrappers are described on the Toolchains page.
Lua
Lua 5.4.8 ships on the developer image as /devtools/bin/lua: the
interpreter and its interactive REPL, built with the Motor toolchain from the unmodified
upstream sources, with no source patches, using Lua's POSIX configuration. It was the
first real C program brought up on the C library, and its acceptance script is part of
the libc test suite.
lua -e 'print(_VERSION)'
lua script.lua
How it is tested
The C library was brought up milestone by milestone, with a test program for each:
toolchain, the runtime shim, mlibc bring-up, stdio and malloc, the filesystem, threads
and TLS, sockets, poll/select and signals with Lua as the real program, and the C++
stack. The plan and the record of each step are in docs/porting-libc/ in the
repository. The developer-image test run (src/tests/full-test-dev.sh) compiles
and runs the shipped C and C++ samples natively in a VM, including a check that
fstat answers correctly for files, sockets, and terminal descriptors and that
the temporary-directory contract holds for both C and std::filesystem.