# What Platforms Can Maru Run On? libc, Linux, and Bare‑Metal Support Explained

> Discover Maru's platform support for libc, Linux, and bare-metal execution. Run Maru without a C runtime on minimal C libraries or directly on the Linux kernel.

- Repository: [Attila Lendvai/maru](https://github.com/attila-lendvai/maru)
- Tags: support
- Published: 2026-02-25

---

**Maru supports three distinct platform abstractions—libc, Linux, and metacircular—that enable it to execute on bare‑metal hardware, minimal C library environments, or directly atop the Linux kernel without any C runtime.**

Maru is a minimalist, self‑hosting Lisp implementation designed for deep systems programming and maximum portability. According to the attila-lendvai/maru source code, the runtime selects its execution environment at build‑time through a configurable platform layer that abstracts away the underlying hardware and OS dependencies.

## The Three Supported Platform Targets

Maru defines its supported platforms in [`doc/platforms.md`](https://github.com/attila-lendvai/maru/blob/main/doc/platforms.md) and the `Makefile` `PLATFORMS` variable. Each target represents a different contract between the Maru evaluator and its host environment.

- **libc**: Assumes only a von Neumann CPU with memory and a standard C library. This platform requires no operating system services and can target bare‑metal environments by linking against lightweight libc implementations like musl or newlib.

- **linux**: Assumes the Linux kernel as the sole primitive. This platform uses direct `syscall` invocations and explicitly avoids linking against libc or any C language abstractions, making it ideal for containerized or minimal Linux images.

- **metacircular**: An experimental work‑in‑progress where the "holding universe" is another Maru VM. This platform enables self‑hosting scenarios and research into bootstrapping Maru within itself.

## How Platform Selection Works

The build system selects platform‑specific code through the `PLATFORM` make variable, which defaults to `libc` as defined in the `Makefile` around line 35‑38.

When you invoke `make`, the system loads `source/platforms/load-platform.l`, which dispatches to the appropriate platform loader based on the `PLATFORM` value. Each platform provides a loader file at `source/platforms/<name>/<name>.l` (e.g., `source/platforms/libc/libc.l` or `source/platforms/linux/linux.l`) that performs two critical tasks:

1. Sets the feature flag `(set (feature platform/<name>) true)` to enable platform‑specific code paths in the core evaluator.
2. Declares C‑implemented primitives via `(define-C-functions …)` to expose platform capabilities—whether libc wrappers or raw Linux syscalls.

The platform‑agnostic VM core in `source/evaluator/*.l` remains unchanged across targets; it simply checks the `platform/<name>` feature flag to conditionally execute platform‑specific logic.

## The libc Platform: Bare‑Metal and Minimal Environments

The `libc` platform in `source/platforms/libc/libc.l` provides the most flexible deployment option. Because it assumes only a C library and basic memory, you can run Maru on bare‑metal hardware by supplying a minimal startup stub (similar to `crt0`) and linking against embedded‑friendly libc implementations.

For true bare‑metal builds, you can apply compiler flags such as `-nostdlib -nostartfiles -ffreestanding` (used for the `linux` target but equally applicable here) to prevent the linker from including unwanted runtime components. The `Makefile` compiles any C helper files in `source/platforms/libc/*.c` into objects in `$(BUILD_x86)` or `$(BUILD_llvm)` directories, linking them into the final evaluator binary.

## The Linux Platform: Kernel‑Direct System Calls

The `linux` platform in `source/platforms/linux/linux.l` eliminates the C library entirely. Instead of calling `printf` or `malloc`, the evaluator invokes the Linux kernel directly through architecture‑specific `syscall` instructions.

This approach minimizes binary size and removes abstraction overhead, making it suitable for high‑density container deployments or security‑conscious environments where libc attack surfaces must be eliminated. The platform loader defines the necessary C functions to wrap raw syscalls, allowing the Lisp‑level code to remain portable while interacting directly with the kernel.

## The Metacircular Platform: Self‑Hosting Experiments

Located at `source/platforms/metacircular/metacircular.l`, this experimental platform treats another running Maru instance as its host environment. While still a work‑in‑progress, it enables research into self‑hosting and bootstrapping scenarios where a Maru VM hosts a child Maru VM, potentially simplifying cross‑compilation and deep introspection tasks.

## Building Maru for Different Platforms

To compile the evaluator for a specific target, set the `PLATFORM` variable when invoking `make`. The build system uses `source/platforms/run-compiler.l` to initialize the compiler backend regardless of the selected target.

Build for the generic libc platform (suitable for bare‑metal with appropriate linker scripts):

```bash
make PLATFORM=libc eval

```

Build for direct Linux kernel interaction without libc:

```bash
make PLATFORM=linux eval

```

Generate an LLVM‑backend binary for the Linux platform:

```bash
make PLATFORM=linux eval-llvm

```

After building, start the REPL using the platform‑specific evaluator:

```bash
./eval boot.l -

```

The `run` and `run-bare` make targets use `$(TEST_EVAL)` to launch interactive sessions with the correctly configured platform binary.

## Summary

- Maru supports three platform targets defined in [`doc/platforms.md`](https://github.com/attila-lendvai/maru/blob/main/doc/platforms.md): **libc**, **linux**, and **metacircular**.
- Platform selection occurs at build‑time via the `PLATFORM` make variable, processed by `source/platforms/load-platform.l`.
- The **libc** platform runs on bare‑metal hardware by linking against minimal C libraries like musl or newlib.
- The **linux** platform bypasses libc entirely, using direct kernel syscalls for minimal overhead.
- Platform loaders in `source/platforms/<name>/<name>.l` set feature flags and declare C primitives via `(define-C-functions …)`.
- Build commands `make PLATFORM=libc eval` and `make PLATFORM=linux eval` produce binaries tailored to their respective environments.

## Frequently Asked Questions

### Can Maru run on bare metal without an operating system?

Yes. The `libc` platform supports bare‑metal execution by linking against lightweight C library implementations such as musl or newlib. You must provide a minimal startup stub (similar to `crt0`) and can use linker flags like `-nostdlib` and `-ffreestanding` to avoid standard runtime dependencies, as referenced in the platform build configuration.

### What is the difference between the libc and linux platforms?

The `libc` platform assumes a standard C library is available and can run on any environment providing libc, including bare‑metal with embedded libraries. The `linux` platform assumes only the Linux kernel is present and makes raw `syscall` invocations without any C library support, resulting in smaller binaries but requiring manual kernel interface management.

### How do I select a platform when building Maru?

Set the `PLATFORM` make variable when invoking the build system. The default is `libc`, but you can specify `linux` or `metacircular` explicitly. For example, `make PLATFORM=linux eval` builds an evaluator that interfaces directly with the Linux kernel, while `make PLATFORM=libc eval` produces a binary suitable for generic C library environments.

### Does Maru require a C library to run?

It depends on the platform. The `libc` platform requires a C library (though it can be minimal). The `linux` platform requires no C library and interfaces directly with the kernel. The core VM in `source/evaluator/*.l` contains no C dependencies itself; all platform integration happens through the loader mechanism in `source/platforms/load-platform.l`.