Merge #268

268: Add the libtock_runtime crate and platform switching. r=hudson-ayers a=jrvanwhy

This is the first `libtock_runtime` PR. It contains:

A. `Cargo.toml` for `libtock_runtime`
B. Automatic linker script identification.

The automatic linker script identification is based on `libtock-rs`'s current `build.rs` (in the root of the repository), but makes the following improvements:

A. Better supports out-of-tree platforms by adding a cargo feature to disable linker script identification. `libtock-rs`'s build.rs always emits a warning of the PLATFORM variable is not specified.
B. Improves error handling, particularly around builds in paths we cannot support (e.g. if `cargo` is executed in a directory whose name contains a newline character).
C. Moves the layout file location into `cargo`'s `OUT_DIR`, which is the only directory build scripts are *supposed* to modify.
D. Removes support for the `platform` file to select a platform. `libtock-rs` looked for a `platform` file in its own source to identify the platform it is run in -- I'm not really sure how this was supposed to be used.

Co-authored-by: Johnathan Van Why <jrvanwhy@google.com>
diff --git a/.gitignore b/.gitignore
index ae858ab..2bcb93f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
 /Cargo.lock
-/layout.ld
 /platform
 /target
diff --git a/Cargo.toml b/Cargo.toml
index 99543d9..89185d4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -83,6 +83,7 @@
     "codegen",
     "core",
     "core/platform",
+    "core/runtime",
     "test_runner",
     "tools/print_sizes",
 ]
diff --git a/Makefile b/Makefile
index 779a021..da40d28 100644
--- a/Makefile
+++ b/Makefile
@@ -78,9 +78,9 @@
 
 .PHONY: test
 test: examples test-qemu-hifive
-	PLATFORM=nrf52 cargo fmt --all -- --check
-	PLATFORM=nrf52 cargo clippy --workspace --all-targets
-	PLATFORM=nrf52 cargo miri test --workspace
+	LIBTOCK_PLATFORM=nrf52 PLATFORM=nrf52 cargo fmt --all -- --check
+	LIBTOCK_PLATFORM=nrf52 PLATFORM=nrf52 cargo clippy --workspace --all-targets
+	LIBTOCK_PLATFORM=nrf52 PLATFORM=nrf52 cargo miri test --workspace
 	echo '[ SUCCESS ] libtock-rs tests pass'
 
 .PHONY: analyse-stack-sizes
diff --git a/build.rs b/build.rs
index b24d3d2..dc2dbef 100644
--- a/build.rs
+++ b/build.rs
@@ -3,10 +3,11 @@
 use std::fs::File;
 use std::io::BufRead;
 use std::io::BufReader;
-use std::path::Path;
+use std::path::{Path, PathBuf};
 use std::process;
 
 static LAYOUT_FILE_NAME: &str = "layout.ld";
+static LAYOUT_GENERIC_FILENAME: &str = "layout_generic.ld";
 
 fn main() {
     static PLATFORM_ENV_VAR: &str = "PLATFORM";
@@ -19,6 +20,7 @@
     println!("cargo:rerun-if-env-changed={}", KERNEL_HEAP_SIZE);
     println!("cargo:rerun-if-changed={}", PLATFORM_FILE_NAME);
     println!("cargo:rerun-if-changed={}", LAYOUT_FILE_NAME);
+    println!("cargo:rerun-if-changed={}", LAYOUT_GENERIC_FILENAME);
 
     let platform_name =
         read_env_var(PLATFORM_ENV_VAR).or_else(|| read_board_name_from_file(PLATFORM_FILE_NAME));
@@ -70,5 +72,21 @@
         println!("Cannot find layout file {:?}", path);
         process::exit(1);
     }
-    fs::copy(linker_file_name, LAYOUT_FILE_NAME).unwrap();
+    // Note: cargo fails if run in a path that is not valid Unicode, so this
+    // script doesn't need to handle non-Unicode paths. Also, OUT_DIR cannot be
+    // in a location with a newline in it, or we have no way to pass
+    // rustc-link-search to cargo.
+    let out_dir = &std::env::var("OUT_DIR").expect("Unable to read OUT_DIR");
+    assert!(
+        !out_dir.contains('\n'),
+        "Build path contains a newline, which is unsupported"
+    );
+    let out_layout_path: PathBuf = [out_dir, "layout.ld"].iter().collect();
+    fs::copy(linker_file_name, out_layout_path).unwrap();
+
+    // Copy the generic layout file into OUT_DIR.
+    let out_layout_generic: PathBuf = [out_dir, LAYOUT_GENERIC_FILENAME].iter().collect();
+    fs::copy(LAYOUT_GENERIC_FILENAME, out_layout_generic)
+        .expect("Unable to copy layout_generic.ld into OUT_DIR");
+    println!("cargo:rustc-link-search={}", out_dir);
 }
diff --git a/core/examples/empty_main.rs b/core/examples/empty_main.rs
deleted file mode 100644
index a3644b7..0000000
--- a/core/examples/empty_main.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-// The most minimal libtock_core example possible. This file primarily exists
-// for code size measurement, as this should create the smallest-possible
-// libtock_core app.
-
-#![no_std]
-
-// If you don't *use* anything from libtock_core directly, cargo will not link
-// it into the executable. However, we still need the runtime and lang items.
-// Therefore a libtock_core app that doesn't directly mention anything in
-// libtock_core needs to explicitly declare its dependency on libtock_core as
-// follows.
-extern crate libtock_core;
-
-libtock_core::stack_size! {0x400}
-
-fn main() {}
diff --git a/core/runtime/Cargo.toml b/core/runtime/Cargo.toml
new file mode 100644
index 0000000..8da4f70
--- /dev/null
+++ b/core/runtime/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+authors = ["Tock Project Developers <tock-dev@googlegroups.com>"]
+categories = ["embedded", "no-std", "os"]
+description = """libtock-rs runtime. Provides raw system call implementations \
+                 and language items necessary for Tock apps."""
+edition = "2018"
+license = "Apache-2.0 OR MIT"
+name = "libtock_runtime"
+repository = "https://www.github.com/tock/libtock-rs"
+version = "0.1.0"
+
+[dependencies]
+libtock_platform = { path = "../platform" }
+
+[features]
+# By default, libtock_runtime looks for the LIBTOCK_PLATFORM variable to decide
+# what layout file to use. If you are providing your own linker script, set
+# no_auto_layout to disable the layout file logic.
+no_auto_layout = []
diff --git a/core/runtime/build.rs b/core/runtime/build.rs
new file mode 100644
index 0000000..d0281b5
--- /dev/null
+++ b/core/runtime/build.rs
@@ -0,0 +1,55 @@
+use std::fs::copy;
+use std::path::PathBuf;
+
+// auto_layout() identifies the correct linker scripts to use based on the
+// LIBTOCK_PLATFORM environment variable, and copies the linker scripts into
+// OUT_DIR. The cargo invocation must pass -C link-arg=-Tlayout.ld to rustc
+// (using the rustflags cargo config).
+#[cfg(not(feature = "no_auto_layout"))]
+fn auto_layout(out_dir: &str) {
+    const PLATFORM_CFG_VAR: &str = "LIBTOCK_PLATFORM";
+    const LAYOUT_GENERIC_FILENAME: &str = "layout_generic.ld";
+
+    // Note: we need to print these rerun-if commands before using the variable
+    // or file, so that if the build script fails cargo knows when to re-run it.
+    println!("cargo:rerun-if-env-changed={}", PLATFORM_CFG_VAR);
+
+    // Read configuration from environment variables.
+
+    // Read the platform environment variable as a String (our platform names
+    // should all be valid UTF-8).
+    let platform = std::env::var(PLATFORM_CFG_VAR).expect("Please specify LIBTOCK_PLATFORM");
+
+    // Copy the platform-specific layout file into OUT_DIR.
+    let platform_filename = format!("{}.ld", platform);
+    let platform_path: PathBuf = ["layouts", &platform_filename].iter().collect();
+    println!("cargo:rerun-if-changed={}", platform_path.display());
+    assert!(platform_path.exists(), "Unknown platform {}", platform);
+    let out_platform_path: PathBuf = [out_dir, "layout.ld"].iter().collect();
+    copy(&platform_path, out_platform_path).expect("Unable to copy platform layout into OUT_DIR");
+
+    // Copy the generic layout file into OUT_DIR.
+    let out_layout_generic: PathBuf = [out_dir, LAYOUT_GENERIC_FILENAME].iter().collect();
+    println!("cargo:rerun-if-changed={}", LAYOUT_GENERIC_FILENAME);
+    copy(LAYOUT_GENERIC_FILENAME, out_layout_generic)
+        .expect("Unable to copy layout_generic.ld into OUT_DIR");
+}
+
+fn main() {
+    // Note: cargo fails if run in a path that is not valid Unicode, so this
+    // script doesn't need to handle non-Unicode paths. Also, OUT_DIR cannot be
+    // in a location with a newline in it, or we have no way to pass
+    // rustc-link-search to cargo.
+    let out_dir = &std::env::var("OUT_DIR").expect("Unable to read OUT_DIR");
+    assert!(
+        !out_dir.contains('\n'),
+        "Build path contains a newline, which is unsupported"
+    );
+
+    #[cfg(not(feature = "no_auto_layout"))]
+    auto_layout(out_dir);
+
+    // This link search path is used by both auto_layout() and extern_asm().
+    // TODO: Add external assembly and extern_asm().
+    println!("cargo:rustc-link-search={}", out_dir);
+}
diff --git a/core/runtime/layout_generic.ld b/core/runtime/layout_generic.ld
new file mode 100644
index 0000000..0bbef87
--- /dev/null
+++ b/core/runtime/layout_generic.ld
@@ -0,0 +1,171 @@
+/* Userland Generic Layout
+ *
+ * Currently, due to incomplete ROPI-RWPI support in rustc (see
+ * https://github.com/tock/libtock-rs/issues/28), this layout implements static
+ * linking. An application init script must define the FLASH and SRAM address
+ * ranges as well as MPU_MIN_ALIGN before including this layout file.
+ *
+ * Here is a an example application linker script to get started:
+ *     MEMORY {
+ *         /* FLASH memory region must start immediately *after* the Tock
+ *          * Binary Format headers, which means you need to offset the
+ *          * beginning of FLASH memory region relative to where the
+ *          * application is loaded.
+ *         FLASH (rx) : ORIGIN = 0x10030, LENGTH = 0x0FFD0
+ *         SRAM (RWX) : ORIGIN = 0x20000, LENGTH = 0x10000
+ *     }
+ *     MPU_MIN_ALIGN = 8K;
+ *     INCLUDE ../libtock-rs/layout.ld
+ */
+
+ENTRY(_start)
+
+SECTIONS {
+    /* Section for just the app crt0 header.
+     * This must be first so that the app can find it.
+     */
+    .crt0_header :
+    {
+        _beginning = .; /* Start of the app in flash. */
+        /**
+         * Populate the header expected by `crt0`:
+         *
+         *  struct hdr {
+         *    uint32_t got_sym_start;
+         *    uint32_t got_start;
+         *    uint32_t got_size;
+         *    uint32_t data_sym_start;
+         *    uint32_t data_start;
+         *    uint32_t data_size;
+         *    uint32_t bss_start;
+         *    uint32_t bss_size;
+         *    uint32_t reldata_start;
+         *    uint32_t stack_size;
+         *  };
+         */
+        /* Offset of GOT symbols in flash */
+        LONG(LOADADDR(.got) - _beginning);
+        /* Offset of GOT section in memory */
+        LONG(_got);
+        /* Size of GOT section */
+        LONG(SIZEOF(.got));
+        /* Offset of data symbols in flash */
+        LONG(LOADADDR(.data) - _beginning);
+        /* Offset of data section in memory */
+        LONG(_data);
+        /* Size of data section */
+        LONG(SIZEOF(.data));
+        /* Offset of BSS section in memory */
+        LONG(_bss);
+        /* Size of BSS section */
+        LONG(SIZEOF(.bss));
+        /* First address offset after program flash, where elf2tab places
+         * .rel.data section */
+        LONG(LOADADDR(.endflash) - _beginning);
+        /* The size of the stack requested by this application */
+        LONG(_stack_top_aligned - _sstack);
+        /* Pad the header out to a multiple of 32 bytes so there is not a gap
+         * between the header and subsequent .data section. It's unclear why,
+         * but LLD is aligning sections to a multiple of 32 bytes. */
+        . = ALIGN(32);
+    } > FLASH =0xFF
+
+    /* Text section, Code! */
+    .text :
+    {
+        . = ALIGN(4);
+        _text = .;
+        KEEP (*(.start))
+        *(.text*)
+        *(.rodata*)
+        KEEP (*(.syscalls))
+        *(.ARM.extab*)
+        . = ALIGN(4); /* Make sure we're word-aligned here */
+        _etext = .;
+    } > FLASH =0xFF
+
+    /* Application stack */
+    .stack (NOLOAD) :
+    {
+        /* elf2tab requires that the `_sram_origin` symbol be present to
+         * mark the first address in the SRAM memory. Since ELF files do
+         * not really need to specify this address as they only care about
+         * loading into flash, we need to manually mark this address for
+         * elf2tab. elf2tab will use it to add a fixed address header in the
+         * TBF header if needed.
+         */
+        _sram_origin = .;
+        _sstack = .;
+        KEEP(*(.stack_buffer))
+        _stack_top_unaligned = .;
+        . = ALIGN(8);
+        _stack_top_aligned = .;
+    } > SRAM
+
+    /* Data section, static initialized variables
+     *  Note: This is placed in Flash after the text section, but needs to be
+     *  moved to SRAM at runtime
+     */
+    .data : AT (_etext)
+    {
+        . = ALIGN(4); /* Make sure we're word-aligned here */
+        _data = .;
+        KEEP(*(.data*))
+        *(.sdata*) /* RISC-V small-pointer data section */
+        . = ALIGN(4); /* Make sure we're word-aligned at the end of flash */
+    } > SRAM
+
+    /* Global Offset Table */
+    .got :
+    {
+        . = ALIGN(4); /* Make sure we're word-aligned here */
+        _got = .;
+        *(.got*)
+        *(.got.plt*)
+        . = ALIGN(4);
+    } > SRAM
+
+    /* BSS section, static uninitialized variables */
+    .bss :
+    {
+        . = ALIGN(4); /* Make sure we're word-aligned here */
+        _bss = .;
+        KEEP(*(.bss* .sbss*))
+        *(COMMON)
+        . = ALIGN(4);
+    } > SRAM
+
+    /* End of flash. */
+    .endflash :
+    {
+    } > FLASH
+
+    /* ARM Exception support
+     *
+     * This contains compiler-generated support for unwinding the stack,
+     * consisting of key-value pairs of function addresses and information on
+     * how to unwind stack frames.
+     * https://wiki.linaro.org/KenWerner/Sandbox/libunwind?action=AttachFile&do=get&target=libunwind-LDS.pdf
+     *
+     * .ARM.exidx is sorted, so has to go in its own output section.
+     *
+     * __NOTE__: It's at the end because we currently don't actually serialize
+     * it to the binary in elf2tbf. If it was before the RAM sections, it would
+     * through off our calculations of the header.
+     */
+    PROVIDE_HIDDEN (__exidx_start = .);
+    .ARM.exidx :
+    {
+      /* (C++) Index entries for section unwinding */
+      *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+    } > FLASH
+    PROVIDE_HIDDEN (__exidx_end = .);
+
+    /DISCARD/ :
+    {
+      *(.eh_frame)
+    }
+}
+
+ASSERT((_stack_top_aligned - _stack_top_unaligned) == 0, "
+STACK_SIZE must be 8 byte multiple")
diff --git a/core/runtime/layouts/apollo3.ld b/core/runtime/layouts/apollo3.ld
new file mode 100644
index 0000000..8713c27
--- /dev/null
+++ b/core/runtime/layouts/apollo3.ld
@@ -0,0 +1,11 @@
+/* Layout for the Apollo3 MCU, used by the examples in this repository. */
+
+MEMORY {
+  /* The application region is 64 bytes (0x40) */
+  FLASH (rx) : ORIGIN = 0x00040040, LENGTH = 0x0005FFC0
+  SRAM (rwx) : ORIGIN = 0x10002000, LENGTH = 0x2000
+}
+
+MPU_MIN_ALIGN = 8K;
+
+INCLUDE layout_generic.ld
diff --git a/core/runtime/layouts/hail.ld b/core/runtime/layouts/hail.ld
new file mode 100644
index 0000000..179a883
--- /dev/null
+++ b/core/runtime/layouts/hail.ld
@@ -0,0 +1,11 @@
+/* Layout for the Hail board, used by the examples in this repository. */
+
+MEMORY {
+  /* The application region is 64 bytes (0x40) */
+  FLASH (rx) : ORIGIN = 0x00030040, LENGTH = 0x0005FFC0
+  SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 62K
+}
+
+MPU_MIN_ALIGN = 8K;
+
+INCLUDE layout_generic.ld
diff --git a/core/runtime/layouts/hifive1.ld b/core/runtime/layouts/hifive1.ld
new file mode 100644
index 0000000..20294ce
--- /dev/null
+++ b/core/runtime/layouts/hifive1.ld
@@ -0,0 +1,18 @@
+/* Layout for the RISC-V 32 boards, used by the examples in this repository. */
+
+MEMORY {
+  /*
+   * The TBF header can change in size so use 0x40 combined with
+   * --protected-region-size with elf2tab to cover a header upto that
+   * size.
+   *
+   * Note that the SRAM address may need to be changed depending on
+   * the kernel binary, check for the actual address of APP_MEMORY!
+   */
+  FLASH (rx) : ORIGIN = 0x20040040, LENGTH = 32M
+  SRAM (rwx) : ORIGIN = 0x80002400, LENGTH = 0x1C00
+}
+
+MPU_MIN_ALIGN = 1K;
+
+INCLUDE layout_generic.ld
diff --git a/core/runtime/layouts/imxrt1050.ld b/core/runtime/layouts/imxrt1050.ld
new file mode 100644
index 0000000..3e7f904
--- /dev/null
+++ b/core/runtime/layouts/imxrt1050.ld
@@ -0,0 +1,11 @@
+/* Layout for the iMX.RT1050 board, used by the examples in this repository. */
+
+MEMORY {
+  /* The application region is 64 bytes (0x40) */
+  FLASH (rx) : ORIGIN = 0x63002040, LENGTH = 0xFFFFC0
+  SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 112K
+}
+
+MPU_MIN_ALIGN = 8K;
+
+INCLUDE layout_generic.ld
diff --git a/core/runtime/layouts/msp432.ld b/core/runtime/layouts/msp432.ld
new file mode 100644
index 0000000..2c9d2a4
--- /dev/null
+++ b/core/runtime/layouts/msp432.ld
@@ -0,0 +1,9 @@
+MEMORY {
+  /* The application region is 64 bytes (0x40) */
+  FLASH (rx) : ORIGIN = 0x00020040, LENGTH = 0x0001FFC0
+  SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 0x2000
+}
+
+MPU_MIN_ALIGN = 8K;
+
+INCLUDE layout_generic.ld
diff --git a/core/runtime/layouts/nrf52.ld b/core/runtime/layouts/nrf52.ld
new file mode 100644
index 0000000..942e86b
--- /dev/null
+++ b/core/runtime/layouts/nrf52.ld
@@ -0,0 +1,11 @@
+/* Layout for the nRF52-DK, used by the examples in this repository. */
+
+MEMORY {
+  /* The application region is 64 bytes (0x40) */
+  FLASH (rx) : ORIGIN = 0x00030040, LENGTH = 0x0005FFC0
+  SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 62K
+}
+
+MPU_MIN_ALIGN = 8K;
+
+INCLUDE layout_generic.ld
diff --git a/core/runtime/layouts/nrf52840.ld b/core/runtime/layouts/nrf52840.ld
new file mode 100644
index 0000000..31bf346
--- /dev/null
+++ b/core/runtime/layouts/nrf52840.ld
@@ -0,0 +1,11 @@
+/* Layout for the nRF52840-DK, usable by the examples in this repository. */
+
+MEMORY {
+  /* The application region is 64 bytes (0x40) */
+  FLASH (rx) : ORIGIN = 0x00030040, LENGTH = 0x000CFFC0
+  SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 62K
+}
+
+MPU_MIN_ALIGN = 8K;
+
+INCLUDE layout_generic.ld
diff --git a/core/runtime/layouts/nucleo_f429zi.ld b/core/runtime/layouts/nucleo_f429zi.ld
new file mode 100644
index 0000000..3e407b4
--- /dev/null
+++ b/core/runtime/layouts/nucleo_f429zi.ld
@@ -0,0 +1,11 @@
+/* Layout for the Nucleo F429zi, used by the examples in this repository. */
+
+MEMORY {
+  /* The application region is 64 bytes (0x40) */
+  FLASH (rx) : ORIGIN = 0x08040040, LENGTH = 255K
+  SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 112K
+}
+
+MPU_MIN_ALIGN = 8K;
+
+INCLUDE layout_generic.ld
diff --git a/core/runtime/layouts/nucleo_f446re.ld b/core/runtime/layouts/nucleo_f446re.ld
new file mode 100644
index 0000000..83e698b
--- /dev/null
+++ b/core/runtime/layouts/nucleo_f446re.ld
@@ -0,0 +1,11 @@
+/* Layout for the Nucleo F446re, used by the examples in this repository. */
+
+MEMORY {
+  /* The application region is 64 bytes (0x40) */
+  FLASH (rx) : ORIGIN = 0x08040040, LENGTH = 255K
+  SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 176K
+}
+
+MPU_MIN_ALIGN = 8K;
+
+INCLUDE layout_generic.ld
diff --git a/core/runtime/layouts/opentitan.ld b/core/runtime/layouts/opentitan.ld
new file mode 100644
index 0000000..ada781c
--- /dev/null
+++ b/core/runtime/layouts/opentitan.ld
@@ -0,0 +1,18 @@
+/* Layout for the RISC-V 32 boards, used by the examples in this repository. */
+
+MEMORY {
+  /*
+   * The TBF header can change in size so use 0x40 combined with
+   * --protected-region-size with elf2tab to cover a header upto that
+   * size.
+   *
+   * Note that the SRAM address may need to be changed depending on
+   * the kernel binary, check for the actual address of APP_MEMORY!
+   */
+  FLASH (rx) : ORIGIN = 0x20030040, LENGTH = 32M
+  SRAM (rwx) : ORIGIN = 0x10004000, LENGTH = 512K
+}
+
+MPU_MIN_ALIGN = 1K;
+
+INCLUDE layout_generic.ld
diff --git a/core/runtime/layouts/stm32f3discovery.ld b/core/runtime/layouts/stm32f3discovery.ld
new file mode 100644
index 0000000..9368003
--- /dev/null
+++ b/core/runtime/layouts/stm32f3discovery.ld
@@ -0,0 +1,11 @@
+/* Layout for the stm32f3discovery board, usable by the examples in this repository. */
+
+MEMORY {
+  /* The application region is 64 bytes (0x40) */
+  FLASH (rx) : ORIGIN = 0x08020040, LENGTH = 0x00020000
+  SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 48K
+}
+
+MPU_MIN_ALIGN = 8K;
+
+INCLUDE layout_generic.ld
diff --git a/core/runtime/src/lib.rs b/core/runtime/src/lib.rs
new file mode 100644
index 0000000..d7ea623
--- /dev/null
+++ b/core/runtime/src/lib.rs
@@ -0,0 +1,21 @@
+//! `libtock_runtime` provides the runtime for Tock process binaries written in
+//! Rust as well as interfaces to Tock's system calls.
+//!
+//! `libtock_runtime` is designed for statically-compiled binaries, and needs to
+//! know the location (in non-volatile memory and RAM) at which the process will
+//! execute. It reads the `LIBTOCK_PLATFORM` variable to determine what location
+//! to build for (see the `layouts/` directory to see what platforms are
+//! available). It expects the following cargo config options to be set (e.g. in
+//! `.cargo/config`):
+//! ```
+//! [build]
+//! rustflags = [
+//!     "-C", "relocation-model=static",
+//!     "-C", "link-arg=-Tlayout.ld",
+//! ]
+//! ```
+//! If a process binary wants to support another platform, it can set the
+//! `no_auto_layout` feature on `libtock_runtime` to disable this functionality
+//! and provide its own layout file.
+
+#![no_std]