Migrate the assembler invocation into build.rs, and deal with the fallout.

Review feedback indicated I should build libtock_runtime's external assembly files in build.rs rather than checking in a compiled binary. I added the compilation logic to build.rs and removed the assembly script and GitHub workflow.

Adding the compilation logic exposed a layout configuration bug: rust-lld was still picking up generic_layout.ld from the root of the repository! I realize now that the linker script namespace is global rather than relative. As such, I renamed the file to "libtock_layout.ld", so as to prevent collisions with libtock-rs' generic_layout and application repositorys' files. This revealed that I never updated the per-board layout scripts to work with the new layout, so I made that change as well.

Now that `build.rs` correctly fails when you try to build `libtock_runtime` for unsupported architectures (e.g. x86_64), the clippy and miri invocations in `make test` fail. I reconfigured them so they pass and test all platform-independent crates.

Last, I added a Mac OS workflow to see if the changes are compatible with Mac OS. I expect this to take some more revision.
diff --git a/.github/workflows/artifacts.yml b/.github/workflows/artifacts.yml
index 94a8b10..d394011 100644
--- a/.github/workflows/artifacts.yml
+++ b/.github/workflows/artifacts.yml
@@ -12,6 +12,7 @@
 
       - name: Install dependencies
         run: |
+          sudo apt-get install binutils-riscv64-unknown-elf
           cargo install elf2tab --version 0.4.0
 
       - name: Build Hello World
diff --git a/.github/workflows/assembly.yml b/.github/workflows/assembly.yml
deleted file mode 100644
index 3ac8ceb..0000000
--- a/.github/workflows/assembly.yml
+++ /dev/null
@@ -1,51 +0,0 @@
-# Builds the libtock_runtime entry point assembly, making sure the source code
-# and compiled library match. Outputs the library file as an artifact in case
-# users have a different toolchain version that produces a slightly different
-# library.
-
-name: assembly
-
-# We want to run this on all pull requests. Additionally, Bors needs workflows
-# to run on the `staging` and `trying` branches to block merges on them.
-on:
-  pull_request:
-  push:
-    branches:
-      - staging
-      - trying
-
-jobs:
-  assembly:
-    # Using ubuntu-latest can cause breakage when ubuntu-latest is updated to
-    # point at a new Ubuntu version. Instead, explicitly specify the version, so
-    # we can update when we need to. This *could* break if we don't update it
-    # until support for 20.04 is dropped, but it is likely we'll have a reason
-    # to update to a newer Ubuntu before then anyway.
-    runs-on: ubuntu-20.04
-
-    steps:
-      # Clones a single commit from the libtock-rs repository. The commit cloned
-      # is a merge commit between the PR's target branch and the PR's source.
-      # We'll later add another commit (the pre-merge target branch) to the
-      # repository.
-      - name: Clone repository
-        uses: actions/checkout@v2.3.0
-
-      # Build the entry point assembly.
-      - name: Build assembly
-        run: |
-          sudo apt-get install binutils-riscv64-linux-gnu
-          core/runtime/asm/assemble.sh
-
-      # Publish the built assembly library.
-      - name: Upload assembly library
-        uses: actions/upload-artifact@v2
-        with:
-          name: assembly
-          path: core/runtime/asm/*.a
-
-      # The main goal of this is to verify the built library matches the PR's
-      # library, although this check has the secondary benefit of catching any
-      # intermediate files left behind by assemble.sh
-      - name: Check assembly library diff
-        run: git diff --quiet
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5bdae5f..6631920 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -37,6 +37,7 @@
       # GitHub Actions at the time of this writing.
       - name: Build and Test
         run: |
+          sudo apt-get install binutils-riscv64-unknown-elf
           cd "${GITHUB_WORKSPACE}"
           make -j2 setup
           make -j2 test
diff --git a/.github/workflows/mac_os.yml b/.github/workflows/mac_os.yml
new file mode 100644
index 0000000..b04b51b
--- /dev/null
+++ b/.github/workflows/mac_os.yml
@@ -0,0 +1,39 @@
+# This workflow verifies libtock-rs is usable on Mac OS.
+
+name: ci-mac-os
+
+# We want to run CI on all pull requests. Additionally, Bors needs workflows to
+# run on the `staging` and `trying` branches.
+on:
+  pull_request:
+  push:
+    branches:
+      - staging
+      - trying
+
+jobs:
+  ci-mac-os:
+    runs-on: macos-10.15
+
+    steps:
+      # Clones a single commit from the libtock-rs repository. The commit cloned
+      # is a merge commit between the PR's target branch and the PR's source.
+      # Note that we checkout submodules so that we can invoke Tock's CI setup
+      # scripts, but we do not recursively checkout submodules as we need Tock's
+      # makefile to set up the qemu submodule itself.
+      - name: Clone repository
+        uses: actions/checkout@v2.3.0
+        with:
+          submodules: true
+
+      # Install the toolchains we need, the run the Makefile's test action. We
+      # let the makefile do most of the work because the makefile can be tested
+      # locally. Using -j2 because the Actions VMs have 2 cores.
+      - name: Build and Test
+        run: |
+          brew tap riscv/riscv
+          brew update
+          brew install riscv-gnu-toolchain --with-multilib
+          cd "${GITHUB_WORKSPACE}"
+          make -j2 setup
+          make -j2 test
diff --git a/.github/workflows/size-diff.yml b/.github/workflows/size-diff.yml
index cbf2939..b991c5f 100644
--- a/.github/workflows/size-diff.yml
+++ b/.github/workflows/size-diff.yml
@@ -39,6 +39,7 @@
       # master.
       - name: Compute sizes
         run: |
+          sudo apt-get install binutils-riscv64-unknown-elf
           UPSTREAM_REMOTE_NAME="${UPSTREAM_REMOTE_NAME:-origin}"
           GITHUB_BASE_REF="${GITHUB_BASE_REF:-master}"
           cd "${GITHUB_WORKSPACE}"
diff --git a/Makefile b/Makefile
index da40d28..e228ab3 100644
--- a/Makefile
+++ b/Makefile
@@ -79,8 +79,9 @@
 .PHONY: test
 test: examples test-qemu-hifive
 	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
+	PLATFORM=nrf52 cargo clippy --all-targets --exclude libtock_runtime --workspace
+	LIBTOCK_PLATFORM=hifive1 cargo clippy --target=riscv32imac-unknown-none-elf -p libtock_runtime
+	PLATFORM=nrf52 cargo miri test --exclude libtock_runtime --workspace
 	echo '[ SUCCESS ] libtock-rs tests pass'
 
 .PHONY: analyse-stack-sizes
diff --git a/bors.toml b/bors.toml
index 3faebdc..dfae601 100644
--- a/bors.toml
+++ b/bors.toml
@@ -1,5 +1,5 @@
 status = [
-  "assembly",
   "ci",
+  "ci-mac-os",
   "size-diff",
 ]
diff --git a/core/runtime/asm/assemble.sh b/core/runtime/asm/assemble.sh
deleted file mode 100755
index d07e2ab..0000000
--- a/core/runtime/asm/assemble.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-
-set -e
-
-# Switch into the directory this script is in, in case it was run from another
-# location.
-cd "$(dirname "$0")"
-
-# Our CI runs in GitHub Actions' Ubuntu 20.04 image. The only RISC-V toolchain
-# in Ubuntu 20.04's repositories is for riscv64-linux-gnu. Fortunately, this
-# toolchain can output 32-bit RISC-V assembly using the -march= option, and the
-# fact it is targeted at GNU/Linux doesn't matter for our short handwritten
-# assembly segment.
-#
-# Although we also support rv32imac targets, we do not need to separately
-# assemble for it, as asm_riscv32.S does not use atomic instructions.
-riscv64-linux-gnu-as -march=rv32imc asm_riscv32.S -o riscv32.o
-# For some reason, riscv64-linux-gnu-as includes local symbols in its output.
-# This pollutes the output of `objdump`, making debugging more difficult. This
-# strips the extra symbols to keep the disassembly readable.
-riscv64-linux-gnu-strip -K start -K rust_start riscv32.o
-
-# Remove the archive file in case there is something unexpected in it (so that
-# issues cannot persist across calls to this script).
-rm -f libriscv32.a
-# c == do not complain if archive needs to be created
-# r == insert or replace file in archive
-riscv64-linux-gnu-ar cr libriscv32.a riscv32.o
-# Remove riscv32.o as it is an intermediate build artifact.
-rm riscv32.o
diff --git a/core/runtime/asm/libriscv32.a b/core/runtime/asm/libriscv32.a
deleted file mode 100644
index f6139a7..0000000
--- a/core/runtime/asm/libriscv32.a
+++ /dev/null
Binary files differ
diff --git a/core/runtime/build.rs b/core/runtime/build.rs
index d0281b5..347d82c 100644
--- a/core/runtime/build.rs
+++ b/core/runtime/build.rs
@@ -1,6 +1,8 @@
 use std::fs::copy;
 use std::path::PathBuf;
 
+mod extern_asm;
+
 // 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
@@ -8,7 +10,7 @@
 #[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";
+    const LAYOUT_GENERIC_FILENAME: &str = "libtock_layout.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.
@@ -49,7 +51,9 @@
     #[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().
+    extern_asm::build_and_link(out_dir);
+
+    // This link search path is used by both auto_layout() and
+    // extern_asm::build_and_link().
     println!("cargo:rustc-link-search={}", out_dir);
 }
diff --git a/core/runtime/extern_asm.rs b/core/runtime/extern_asm.rs
new file mode 100644
index 0000000..1f9419a
--- /dev/null
+++ b/core/runtime/extern_asm.rs
@@ -0,0 +1,133 @@
+//! Build script module for compiling the external assembly (used for the entry
+//! point) and linking it into the process binary. Requires out_dir to be added
+//! to rustc's link search path.
+
+pub(crate) fn build_and_link(out_dir: &str) {
+    use std::env::var;
+    let arch = var("CARGO_CFG_TARGET_ARCH").expect("Unable to read CARGO_CFG_TARGET_ARCH");
+
+    // Identify the toolchain configurations to try for the target architecture.
+    // We support trying multiple toolchains because not all toolchains are
+    // available on every OS that we want to support development on.
+    let build_configs = match arch.as_str() {
+        "riscv32" => &[
+            // First try riscv64-unknown-elf, as it is the toolchain used by
+            // libtock-c.
+            AsmBuildConfig {
+                triple: "riscv64-unknown-elf",
+                as_extra_args: &["-march=rv32imc"],
+                strip: true,
+            },
+            // Second try riscv64-linux-gnu, as riscv64-unknown-elf is
+            // unavailable on Debian 10.
+            AsmBuildConfig {
+                triple: "riscv64-linux-gnu",
+                as_extra_args: &["-march=rv32imc"],
+                strip: true,
+            },
+        ],
+        unknown_arch => {
+            panic!("Unsupported architecture {}", unknown_arch);
+        }
+    };
+
+    // Loop through toolchain configs until one works.
+    for &build_config in build_configs {
+        if try_build(&arch, build_config, out_dir).is_ok() {
+            return;
+        }
+    }
+}
+
+#[derive(Clone, Copy)]
+struct AsmBuildConfig {
+    // Triple name, which is prepended to the command names.
+    triple: &'static str,
+
+    // Extra arguments to pass to the assembler.
+    as_extra_args: &'static [&'static str],
+
+    // Do we need to strip the object file before packing it into the library
+    // archive? This should be set to true on platforms where the assembler adds
+    // local symbols to the object file.
+    strip: bool,
+}
+
+// Indicates the toolchain in the build config is unavailable.
+struct ToolchainUnavailable;
+
+fn try_build(
+    arch: &str,
+    build_config: AsmBuildConfig,
+    out_dir: &str,
+) -> Result<(), ToolchainUnavailable> {
+    use std::path::PathBuf;
+    use std::process::Command;
+
+    // Invoke the assembler to produce an object file.
+    let asm_source = &format!("asm/asm_{}.S", arch);
+    let obj_file_path = [out_dir, "libtock_rt_asm.o"].iter().collect::<PathBuf>();
+    let obj_file = obj_file_path.to_str().expect("Non-Unicode obj_file_path");
+    let as_result = Command::new(format!("{}-as", build_config.triple))
+        .args(build_config.as_extra_args)
+        .args(&[asm_source, "-o", obj_file])
+        .status();
+
+    match as_result {
+        Err(error) => {
+            if error.kind() == std::io::ErrorKind::NotFound {
+                // This `as` command does not exist. Return an error so
+                // build_an_link can try another config (if one is available).
+                return Err(ToolchainUnavailable);
+            } else {
+                panic!("Error invoking assembler: {}", error);
+            }
+        }
+        Ok(status) => {
+            assert!(status.success(), "Assembler returned an error");
+        }
+    }
+
+    // At this point, we know this toolchain is installed. We will fail if later
+    // commands are uninstalled rather than trying a different build config.
+
+    println!("cargo:rerun-if-changed={}", asm_source);
+
+    // Run `strip` if necessary.
+    if build_config.strip {
+        let strip_cmd = format!("{}-strip", build_config.triple);
+        let status = Command::new(&strip_cmd)
+            .args(&["-K", "start", "-K", "rust_start", obj_file])
+            .status()
+            .unwrap_or_else(|_| panic!("Failed to invoke {}", strip_cmd));
+        assert!(status.success(), "{} returned an error", strip_cmd);
+    }
+
+    // Remove the archive file in case there is something unexpected in it. This
+    // prevents issues from persisting across invocations of this script.
+    const ARCHIVE_NAME: &str = "tock_rt_asm";
+    let archive_path: PathBuf = [out_dir, &format!("lib{}.a", ARCHIVE_NAME)]
+        .iter()
+        .collect();
+    if let Err(error) = std::fs::remove_file(&archive_path) {
+        if error.kind() != std::io::ErrorKind::NotFound {
+            panic!("Unable to remove archive file {}", archive_path.display());
+        }
+    }
+
+    // Create the library archive.
+    let ar_cmd = format!("{}-ar", build_config.triple);
+    let archive = archive_path.to_str().expect("Non-Unicode archive_path");
+    let status = std::process::Command::new(&ar_cmd)
+        // c == Do not complain if archive needs to be created.
+        // r == Insert or replace file in archive.
+        .args(&["cr", archive, obj_file])
+        .status()
+        .unwrap_or_else(|_| panic!("Failed to invoke {}", ar_cmd));
+    assert!(status.success(), "{} returned an error", ar_cmd);
+
+    // Tell rustc to link the binary against the library archive.
+    println!("cargo:rustc-link-lib=static={}", ARCHIVE_NAME);
+
+    Ok(())
+}
diff --git a/core/runtime/layouts/apollo3.ld b/core/runtime/layouts/apollo3.ld
index 8713c27..464cf98 100644
--- a/core/runtime/layouts/apollo3.ld
+++ b/core/runtime/layouts/apollo3.ld
@@ -1,11 +1,9 @@
 /* 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
+  FLASH (X) : ORIGIN = 0x00040000, LENGTH = 0x00060000
+  RAM   (W) : ORIGIN = 0x10002000, LENGTH = 0x2000
 }
 
-MPU_MIN_ALIGN = 8K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/hail.ld b/core/runtime/layouts/hail.ld
index 179a883..db48142 100644
--- a/core/runtime/layouts/hail.ld
+++ b/core/runtime/layouts/hail.ld
@@ -1,11 +1,9 @@
 /* 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
+  FLASH (X) : ORIGIN = 0x00030000, LENGTH = 0x00060000
+  RAM   (W) : ORIGIN = 0x20004000, LENGTH = 62K
 }
 
-MPU_MIN_ALIGN = 8K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/hifive1.ld b/core/runtime/layouts/hifive1.ld
index 20294ce..78dc51b 100644
--- a/core/runtime/layouts/hifive1.ld
+++ b/core/runtime/layouts/hifive1.ld
@@ -1,18 +1,12 @@
 /* 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
+  /* 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
+  FLASH (X) : ORIGIN = 0x20040000, LENGTH = 32M
+  RAM   (W) : ORIGIN = 0x80002400, LENGTH = 0x1C00
 }
 
-MPU_MIN_ALIGN = 1K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/imxrt1050.ld b/core/runtime/layouts/imxrt1050.ld
index 3e7f904..1458870 100644
--- a/core/runtime/layouts/imxrt1050.ld
+++ b/core/runtime/layouts/imxrt1050.ld
@@ -1,11 +1,9 @@
 /* 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
+  FLASH (X) : ORIGIN = 0x63002000, LENGTH = 0x1000000
+  RAM   (W) : ORIGIN = 0x20004000, LENGTH = 112K
 }
 
-MPU_MIN_ALIGN = 8K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/msp432.ld b/core/runtime/layouts/msp432.ld
index 2c9d2a4..170d4b1 100644
--- a/core/runtime/layouts/msp432.ld
+++ b/core/runtime/layouts/msp432.ld
@@ -1,9 +1,7 @@
 MEMORY {
-  /* The application region is 64 bytes (0x40) */
-  FLASH (rx) : ORIGIN = 0x00020040, LENGTH = 0x0001FFC0
-  SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 0x2000
+  FLASH (X) : ORIGIN = 0x00020000, LENGTH = 0x00020000
+  RAM   (W) : ORIGIN = 0x20004000, LENGTH = 0x2000
 }
 
-MPU_MIN_ALIGN = 8K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/nrf52.ld b/core/runtime/layouts/nrf52.ld
index 942e86b..fbbcdc3 100644
--- a/core/runtime/layouts/nrf52.ld
+++ b/core/runtime/layouts/nrf52.ld
@@ -1,11 +1,9 @@
 /* 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
+  FLASH (X) : ORIGIN = 0x00030000, LENGTH = 0x00060000
+  RAM   (W) : ORIGIN = 0x20004000, LENGTH = 62K
 }
 
-MPU_MIN_ALIGN = 8K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/nrf52840.ld b/core/runtime/layouts/nrf52840.ld
index 31bf346..06b6f6b 100644
--- a/core/runtime/layouts/nrf52840.ld
+++ b/core/runtime/layouts/nrf52840.ld
@@ -1,11 +1,9 @@
 /* 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
+  FLASH (X) : ORIGIN = 0x00030000, LENGTH = 0x000D0000
+  RAM   (W) : ORIGIN = 0x20004000, LENGTH = 62K
 }
 
-MPU_MIN_ALIGN = 8K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/nucleo_f429zi.ld b/core/runtime/layouts/nucleo_f429zi.ld
index 3e407b4..a9c634a 100644
--- a/core/runtime/layouts/nucleo_f429zi.ld
+++ b/core/runtime/layouts/nucleo_f429zi.ld
@@ -1,11 +1,9 @@
 /* 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
+  FLASH (X) : ORIGIN = 0x08040000, LENGTH = 255K
+  RAM   (W) : ORIGIN = 0x20004000, LENGTH = 112K
 }
 
-MPU_MIN_ALIGN = 8K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/nucleo_f446re.ld b/core/runtime/layouts/nucleo_f446re.ld
index 83e698b..d3953f5 100644
--- a/core/runtime/layouts/nucleo_f446re.ld
+++ b/core/runtime/layouts/nucleo_f446re.ld
@@ -1,11 +1,9 @@
 /* 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
+  FLASH (X) : ORIGIN = 0x08040000, LENGTH = 255K
+  RAM   (W) : ORIGIN = 0x20004000, LENGTH = 176K
 }
 
-MPU_MIN_ALIGN = 8K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/opentitan.ld b/core/runtime/layouts/opentitan.ld
index ada781c..50ea340 100644
--- a/core/runtime/layouts/opentitan.ld
+++ b/core/runtime/layouts/opentitan.ld
@@ -1,18 +1,12 @@
 /* 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
+  /* 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
+  FLASH (X) : ORIGIN = 0x20030000, LENGTH = 32M
+  RAM   (W) : ORIGIN = 0x10004000, LENGTH = 512K
 }
 
-MPU_MIN_ALIGN = 1K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layouts/stm32f3discovery.ld b/core/runtime/layouts/stm32f3discovery.ld
index 9368003..05b0b54 100644
--- a/core/runtime/layouts/stm32f3discovery.ld
+++ b/core/runtime/layouts/stm32f3discovery.ld
@@ -1,11 +1,9 @@
 /* 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
+  FLASH (X) : ORIGIN = 0x08020000, LENGTH = 0x00020000
+  RAM   (W) : ORIGIN = 0x20004000, LENGTH = 48K
 }
 
-MPU_MIN_ALIGN = 8K;
-
-INCLUDE layout_generic.ld
+TBF_HEADER_SIZE = 0x40;
+INCLUDE libtock_layout.ld
diff --git a/core/runtime/layout_generic.ld b/core/runtime/libtock_layout.ld
similarity index 100%
rename from core/runtime/layout_generic.ld
rename to core/runtime/libtock_layout.ld